treewide: Debloat samsung knox

Signed-off-by: rsuntk <rissu.ntk@gmail.com>
This commit is contained in:
rsuntk 2025-03-11 10:56:21 +00:00
parent af14a6ca19
commit bdcd65c128
170 changed files with 4 additions and 29351 deletions

View file

@ -1637,68 +1637,6 @@ config SYSVIPC_COMPAT
def_bool y
depends on COMPAT && SYSVIPC
menu "Hypervisor"
config UH
bool "Enable micro hypervisor feature"
depends on !SEC_FACTORY
default n
help
enables a micro hypervisor.
It's samsung's hypervisor.
RKP and etc can be loaded on it.
please check a memory map for it.
config RKP
bool "Enable RKP(Realtime Kernel Protection) feature"
depends on UH
default n
help
protects a kernel text and etc.
config KDP
bool "Enable KDP(Kernel Data Protection) feature"
depends on !SEC_VTS_TEST
depends on UH
select KDP_CRED
select KDP_NS
default n
help
Prevents unauthorized cred modification,
namespace modification, mapping for page table.
config KDP_CRED
bool "Enable KDP(Kernel Data Protection) cred feature"
depends on !SEC_VTS_TEST
depends on KDP
default n
help
Prevents unauthorized cred modification.
config KDP_NS
bool "Enable KDP(Kernel Data Protection) namespace feature"
depends on !SEC_VTS_TEST
depends on KDP
default n
help
Prevents unauthorized namespace modification, mapping for page table.
config RKP_TEST
bool "Enable RKP test"
depends on !SAMSUNG_PRODUCT_SHIP && RKP
default n
help
enables RKP test.
config KDP_TEST
bool "Enable KDP test"
depends on !SAMSUNG_PRODUCT_SHIP && KDP
depends on KDP
default n
help
enables KDP test.
endmenu
menu "Power management options"
source "kernel/power/Kconfig"

View file

@ -196,9 +196,6 @@ obj-$(CONFIG_SENSORS_SSC) += sensors/
obj-$(CONFIG_ADSP_FACTORY) += adsp_factory/
obj-y += switch/
# FastuH
obj-$(CONFIG_UH) += uh/
# SPU signature verify
obj-$(CONFIG_SPU_VERIFY) += spu_verify/

View file

@ -74,18 +74,6 @@
#include <linux/seq_file.h>
#include <linux/uio.h>
#include <linux/skb_array.h>
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
#include <linux/types.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/ip.h>
#include <net/ip.h>
#define META_MARK_BASE_LOWER 100
#define META_MARK_BASE_UPPER 500
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
#include <linux/bpf.h>
#include <linux/bpf_trace.h>
#include <linux/mutex.h>
@ -143,28 +131,6 @@ do { \
#define GOODCOPY_LEN 128
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
/* The KNOX framework marks packets intended to a VPN client for special processing differently.
* The marked packets hit special IP table rules and are routed back to user space using the TUN driver
* for policy based treatment by the VPN client.
* Some VPN clients can make more intelligent decisions based on the UID/PID information.
* For such clients, we mark packets to be in the range >= META_MARK_BASE_LOWER and < META_MARK_BASE_UPPER.
* When such packets are seen, we update the IP headers to carry UID/PID information
* in the IP options - all other packets are ignored.
* Also, see the comments above the individual steps taken in the code for details
*/
/* Metadata header structure */
struct knox_meta_param {
uid_t uid;
pid_t pid;
};
#define TUN_META_HDR_SZ sizeof(struct knox_meta_param)
#define TUN_META_MARK_OFFSET offsetof(struct knox_meta_param, uid)
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
#define FLT_EXACT_COUNT 8
struct tap_filter {
unsigned int count; /* Number of addrs. Zero means disabled */
@ -2037,41 +2003,6 @@ static ssize_t tun_chr_write_iter(struct kiocb *iocb, struct iov_iter *from)
return result;
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
static int get_meta_param_values(struct sk_buff *skb, struct knox_meta_param *metalocal) {
struct skb_shared_info *knox_shinfo = NULL;
if (skb != NULL)
knox_shinfo = skb_shinfo(skb);
else {
#ifdef TUN_DEBUG
pr_err("KNOX: NULL SKB in knoxvpn_process_uidpid");
#endif
return 1;
}
if (knox_shinfo == NULL) {
#ifdef TUN_DEBUG
pr_err("KNOX: knox_shinfo value is null");
#endif
return 1;
}
if (knox_shinfo->knox_mark >= META_MARK_BASE_LOWER && knox_shinfo->knox_mark <= META_MARK_BASE_UPPER) {
metalocal->uid = knox_shinfo->uid;
metalocal->pid = knox_shinfo->pid;
}
if (knox_shinfo != NULL) {
knox_shinfo->uid = knox_shinfo->pid = 0;
knox_shinfo->knox_mark = 0;
}
return 0;
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
static ssize_t tun_put_user_xdp(struct tun_struct *tun,
struct tun_file *tfile,
struct xdp_frame *xdp_frame,
@ -2113,10 +2044,6 @@ static ssize_t tun_put_user(struct tun_struct *tun,
struct iov_iter *iter)
{
struct tun_pi pi = { 0, skb->protocol };
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
struct knox_meta_param metalocal = { 0, 0 };
int meta_param_get_status = 0;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
struct tun_pcpu_stats *stats;
ssize_t total;
int vlan_offset = 0;
@ -2145,29 +2072,6 @@ static ssize_t tun_put_user(struct tun_struct *tun,
return -EFAULT;
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
meta_param_get_status = get_meta_param_values(skb, &metalocal);
if (meta_param_get_status == 1) {
#ifdef TUN_DEBUG
pr_err("KNOX: Error obtaining meta param values");
#endif
} else {
if (tun->flags & TUN_META_HDR) {
#ifdef TUN_DEBUG
pr_err("KNOX: Appending uid: %d and pid: %d", metalocal.uid,
metalocal.pid);
#endif
if (iov_iter_count(iter) < sizeof(struct knox_meta_param)) {
return -EINVAL;
}
total += sizeof(struct knox_meta_param);
if (copy_to_iter(&metalocal, sizeof(struct knox_meta_param), iter) != sizeof(struct knox_meta_param))
return -EFAULT;
}
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
if (vnet_hdr_sz) {
struct virtio_net_hdr gso;
@ -2578,9 +2482,7 @@ static struct proto tun_proto = {
static int tun_flags(struct tun_struct *tun)
{
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
return tun->flags & (TUN_FEATURES | IFF_PERSIST | IFF_TUN | IFF_TAP | IFF_META_HDR);
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
return tun->flags & (TUN_FEATURES | IFF_PERSIST | IFF_TUN | IFF_TAP);
}
static ssize_t tun_show_flags(struct device *dev, struct device_attribute *attr,
@ -2782,13 +2684,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
netif_carrier_on(tun->dev);
tun_debug(KERN_INFO, tun, "tun_set_iff\n");
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
if (ifr->ifr_flags & IFF_META_HDR) {
tun->flags |= TUN_META_HDR;
} else {
tun->flags &= ~TUN_META_HDR;
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
/* Make sure persistent devices do not get stuck in
* xoff state.
@ -2980,11 +2875,6 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
unsigned int ifindex;
int le;
int ret;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
int knox_flag = 0;
int tun_meta_param;
int tun_meta_value;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
bool do_notify = false;
#ifdef CONFIG_ANDROID_PARANOID_NETWORK
@ -3005,11 +2895,8 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
* This is needed because we never checked for invalid flags on
* TUNSETIFF.
*/
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
knox_flag |= IFF_META_HDR;
return put_user(IFF_TUN | IFF_TAP | TUN_FEATURES | knox_flag,
return put_user(IFF_TUN | IFF_TAP | TUN_FEATURES,
(unsigned int __user*)argp);
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
} else if (cmd == TUNSETQUEUE) {
return tun_set_queue(file, &ifr);
} else if (cmd == SIOCGSKNS) {
@ -3198,37 +3085,6 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
ret = -EFAULT;
break;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
case TUNGETMETAPARAM:
if (copy_from_user(&tun_meta_param, argp,
sizeof(tun_meta_param))) {
ret = -EFAULT;
break;
}
ret = 0;
switch (tun_meta_param) {
case TUN_GET_META_HDR_SZ:
tun_meta_value = TUN_META_HDR_SZ;
break;
case TUN_GET_META_MARK_OFFSET:
tun_meta_value = TUN_META_MARK_OFFSET;
break;
default:
ret = -EINVAL;
break;
}
if (!ret) {
if (copy_to_user(argp, &tun_meta_value,
sizeof(tun_meta_value)))
ret = -EFAULT;
}
break;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
case TUNSETVNETHDRSZ:
if (copy_from_user(&vnet_hdr_sz, argp, sizeof(vnet_hdr_sz))) {
ret = -EFAULT;

View file

@ -1,7 +0,0 @@
obj-$(CONFIG_UH) += uh_debug_log.o
# app
obj-$(CONFIG_RKP) += rkp.o
obj-$(CONFIG_KDP) += kdp.o
obj-$(CONFIG_RKP_TEST) += rkp_test.o
obj-$(CONFIG_KDP_TEST) += kdp_test.o

View file

@ -1,685 +0,0 @@
#include <asm-generic/sections.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/slub_def.h>
#include <linux/kdp.h>
#include <linux/cred.h>
#include <linux/security.h>
#include <linux/init_task.h>
#include <../../fs/mount.h>
/* security/selinux/include/objsec.h */
struct task_security_struct {
u32 osid; /* SID prior to last execve */
u32 sid; /* current SID */
u32 exec_sid; /* exec SID */
u32 create_sid; /* fscreate SID */
u32 keycreate_sid; /* keycreate SID */
u32 sockcreate_sid; /* fscreate SID */
void *bp_cred;
};
/* security/selinux/hooks.c */
struct task_security_struct init_sec __kdp_ro;
int kdp_enable __kdp_ro = 0;
static int __check_verifiedboot __kdp_ro = 0;
static int __is_kdp_recovery __kdp_ro = 0;
#define VERITY_PARAM_LENGTH 20
static char verifiedbootstate[VERITY_PARAM_LENGTH];
#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP
extern int selinux_enforcing __kdp_ro_aligned;
extern int ss_initialized __kdp_ro_aligned;
#endif
void __init kdp_init(void)
{
struct kdp_init cred;
memset((void *)&cred, 0, sizeof(kdp_init));
cred._srodata = (u64)__start_rodata;
cred._erodata = (u64)__end_rodata;
#ifdef CONFIG_KDP
cred.init_mm_pgd = (u64)swapper_pg_dir;
#endif
cred.credSize = sizeof(struct cred);
cred.sp_size = sizeof(struct task_security_struct);
cred.pgd_mm = offsetof(struct mm_struct,pgd);
cred.uid_cred = offsetof(struct cred,uid);
cred.euid_cred = offsetof(struct cred,euid);
cred.gid_cred = offsetof(struct cred,gid);
cred.egid_cred = offsetof(struct cred,egid);
cred.bp_pgd_cred = offsetof(struct cred,bp_pgd);
cred.bp_task_cred = offsetof(struct cred,bp_task);
cred.type_cred = offsetof(struct cred,type);
cred.security_cred = offsetof(struct cred,security);
cred.usage_cred = offsetof(struct cred,use_cnt);
cred.cred_task = offsetof(struct task_struct,cred);
cred.mm_task = offsetof(struct task_struct,mm);
cred.pid_task = offsetof(struct task_struct,pid);
cred.rp_task = offsetof(struct task_struct,real_parent);
cred.comm_task = offsetof(struct task_struct,comm);
cred.bp_cred_secptr = offsetof(struct task_security_struct,bp_cred);
#ifndef CONFIG_KDP
cred.verifiedbootstate = (u64)verifiedbootstate;
#endif
#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP
cred.selinux.selinux_enforcing_va = (u64)&selinux_enforcing;
cred.selinux.ss_initialized_va = (u64)&ss_initialized;
#else
cred.selinux.selinux_enforcing_va = 0;
cred.selinux.ss_initialized_va = 0;
#endif
uh_call(UH_APP_KDP, KDP_INIT, (u64)&cred, 0, 0, 0);
}
static int __init verifiedboot_state_setup(char *str)
{
strlcpy(verifiedbootstate, str, sizeof(verifiedbootstate));
if(!strncmp(verifiedbootstate, "orange", sizeof("orange")))
__check_verifiedboot = 1;
return 0;
}
__setup("androidboot.verifiedbootstate=", verifiedboot_state_setup);
static int __init boot_recovery(char *str)
{
int temp = 0;
if (get_option(&str, &temp)) {
__is_kdp_recovery = temp;
return 0;
}
return -EINVAL;
}
early_param("androidboot.boot_recovery", boot_recovery);
inline bool is_kdp_kmem_cache(struct kmem_cache *s)
{
if (s->name &&
(!strncmp(s->name, CRED_JAR_RO, strlen(CRED_JAR_RO)) ||
!strncmp(s->name, TSEC_JAR, strlen(TSEC_JAR)) ||
!strncmp(s->name, VFSMNT_JAR, strlen(VFSMNT_JAR))))
return true;
else
return false;
}
#ifdef CONFIG_KDP_CRED
/*------------------------------------------------
* CRED
*------------------------------------------------*/
struct kdp_usecnt init_cred_use_cnt = {
.kdp_use_cnt = ATOMIC_INIT(4),
.kdp_rcu_head.non_rcu = 0,
.kdp_rcu_head.bp_cred = (void *)0,
.kdp_rcu_head.reflected_cred = (void *)0,
};
static struct kmem_cache *cred_jar_ro;
static struct kmem_cache *tsec_jar;
static struct kmem_cache *usecnt_jar;
/* Dummy constructor to make sure we have separate slabs caches. */
static void cred_ctor(void *data){}
static void sec_ctor(void *data){}
static void usecnt_ctor(void *data){}
void __init kdp_cred_init(void)
{
cred_jar_ro = kmem_cache_create("cred_jar_ro", sizeof(struct cred),
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, cred_ctor);
if (!cred_jar_ro)
panic("Unable to create RO Cred cache\n");
tsec_jar = kmem_cache_create("tsec_jar", sizeof(struct task_security_struct),
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, sec_ctor);
if (!tsec_jar)
panic("Unable to create RO security cache\n");
usecnt_jar = kmem_cache_create("usecnt_jar", sizeof(struct kdp_usecnt),
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, usecnt_ctor);
if (!usecnt_jar)
panic("Unable to create use count jar\n");
uh_call(UH_APP_KDP, JARRO_TSEC_SIZE, (u64)cred_jar_ro->size, (u64)tsec_jar->size, 0, 0);
}
unsigned int kdp_get_usecount(struct cred *cred)
{
if (is_kdp_protect_addr((unsigned long )cred))
return (unsigned int)ROCRED_UC_READ(cred);
else
return atomic_read(&cred->usage);
}
inline struct cred *get_new_cred(struct cred *cred)
{
if (is_kdp_protect_addr((unsigned long)cred))
ROCRED_UC_INC(cred);
else
atomic_inc(&cred->usage);
return cred;
}
inline void put_cred(const struct cred *_cred)
{
struct cred *cred = (struct cred *) _cred;
validate_creds(cred);
if (is_kdp_protect_addr((unsigned long)cred)) {
if (ROCRED_UC_DEC_AND_TEST(cred))
__put_cred(cred);
} else {
if (atomic_dec_and_test(&(cred)->usage))
__put_cred(cred);
}
}
/* match for kernel/cred.c function */
inline void set_cred_subscribers(struct cred *cred, int n)
{
#ifdef CONFIG_DEBUG_CREDENTIALS
atomic_set(&cred->subscribers, n);
#endif
}
/* Check whether the address belong to Cred Area */
bool is_kdp_protect_addr(unsigned long addr)
{
struct kmem_cache *s;
struct page *page;
void *objp = (void *)addr;
if (!objp)
return false;
if (!kdp_enable)
return false;
if ((addr == ((unsigned long)&init_cred)) ||
(addr == ((unsigned long)&init_sec)))
return true;
page = virt_to_head_page(objp);
s = page->slab_cache;
if (s && (s == cred_jar_ro || s == tsec_jar))
return true;
return false;
}
/* We use another function to free protected creds. */
void put_rocred_rcu(struct rcu_head *rcu)
{
struct cred *cred = container_of(rcu, struct ro_rcu_head, rcu)->bp_cred;
if (ROCRED_UC_READ(cred) != 0)
panic("RO_CRED: put_rocred_rcu() sees %p with usage %d\n",
cred, ROCRED_UC_READ(cred));
security_cred_free(cred);
key_put(cred->session_keyring);
key_put(cred->process_keyring);
key_put(cred->thread_keyring);
key_put(cred->request_key_auth);
if (cred->group_info)
put_group_info(cred->group_info);
free_uid(cred->user);
put_user_ns(cred->user_ns);
if(cred->use_cnt)
kmem_cache_free(usecnt_jar,(void *)cred->use_cnt);
kmem_cache_free(cred_jar_ro, cred);
}
/* prepare_ro_creds - Prepare a new set of credentials which is protected by KDP */
struct cred *prepare_ro_creds(struct cred *old, int kdp_cmd, u64 p)
{
u64 pgd = (u64)(current->mm? current->mm->pgd: swapper_pg_dir);
struct cred *new_ro = NULL;
struct cred_param param_data;
void *use_cnt_ptr = NULL;
void *rcu_ptr = NULL;
void *tsec = NULL;
new_ro = kmem_cache_alloc(cred_jar_ro, GFP_KERNEL);
if (!new_ro)
panic("[%d] : kmem_cache_alloc() failed", kdp_cmd);
use_cnt_ptr = kmem_cache_alloc(usecnt_jar, GFP_KERNEL);
if (!use_cnt_ptr)
panic("[%d] : Unable to allocate usage pointer\n", kdp_cmd);
// get_usecnt_rcu
rcu_ptr = &(((struct kdp_usecnt *)use_cnt_ptr)->kdp_rcu_head);
((struct ro_rcu_head*)rcu_ptr)->bp_cred = (void *)new_ro;
tsec = kmem_cache_alloc(tsec_jar, GFP_KERNEL);
if (!tsec)
panic("[%d] : Unable to allocate security pointer\n", kdp_cmd);
// init
memset((void *)&param_data, 0, sizeof(struct cred_param));
param_data.cred = old;
param_data.cred_ro = new_ro;
param_data.use_cnt_ptr = use_cnt_ptr;
param_data.sec_ptr = tsec;
param_data.type = kdp_cmd;
param_data.use_cnt = (u64)p;
uh_call(UH_APP_KDP, PREPARE_RO_CRED, (u64)&param_data, (u64)current, 0, 0);
if (kdp_cmd == CMD_COPY_CREDS) {
if ((new_ro->bp_task != (void *)p) ||
new_ro->security != tsec ||
new_ro->use_cnt != use_cnt_ptr) {
panic("[%d]: KDP Call failed task=0x%lx:0x%lx, sec=0x%lx:0x%lx, usecnt=0x%lx:0x%lx",
kdp_cmd, new_ro->bp_task, (void *)p,
new_ro->security, tsec, new_ro->use_cnt, use_cnt_ptr);
}
} else {
if ((new_ro->bp_task != current) ||
(current->mm && new_ro->bp_pgd != (void *)pgd) ||
(new_ro->security != tsec) ||
(new_ro->use_cnt != use_cnt_ptr)) {
panic("[%d]: KDP Call failed task=0x%lx:0x%lx, sec=0x%lx:0x%lx, usecnt=0x%lx:0x%lx, pgd=0x%lx:0x%lx",
kdp_cmd, new_ro->bp_task, current, new_ro->security, tsec,
new_ro->use_cnt, use_cnt_ptr, new_ro->bp_pgd, (void *)pgd);
}
}
GET_ROCRED_RCU(new_ro)->non_rcu = old->non_rcu;
GET_ROCRED_RCU(new_ro)->reflected_cred = 0;
ROCRED_UC_SET(new_ro, 2);
set_cred_subscribers(new_ro, 0);
get_group_info(new_ro->group_info);
get_uid(new_ro->user);
get_user_ns(new_ro->user_ns);
#ifdef CONFIG_KEYS
key_get(new_ro->session_keyring);
key_get(new_ro->process_keyring);
key_get(new_ro->thread_keyring);
key_get(new_ro->request_key_auth);
#endif
validate_creds(new_ro);
return new_ro;
}
/* security/selinux/hooks.c */
static bool is_kdp_tsec_jar(unsigned long addr)
{
struct kmem_cache *s;
struct page *page;
void *objp = (void *)addr;
if (!objp)
return false;
page = virt_to_head_page(objp);
s = page->slab_cache;
if (s && s == tsec_jar)
return true;
return false;
}
static inline int chk_invalid_kern_ptr(u64 tsec)
{
return (((u64)tsec >> 36) != (u64)0xFFFFFFC);
}
void kdp_free_security(unsigned long tsec)
{
if (!tsec || chk_invalid_kern_ptr(tsec))
return;
if (is_kdp_tsec_jar(tsec))
kmem_cache_free(tsec_jar, (void *)tsec);
else
kfree((void *)tsec);
}
void kdp_assign_pgd(struct task_struct *p)
{
u64 pgd = (u64)(p->mm? p->mm->pgd: swapper_pg_dir);
uh_call(UH_APP_KDP, SET_CRED_PGD, (u64)p->cred, (u64)pgd, 0, 0);
}
struct task_security_struct init_sec __kdp_ro;
static inline unsigned int cmp_sec_integrity(const struct cred *cred, struct mm_struct *mm)
{
if (cred->bp_task != current)
printk(KERN_ERR "[KDP] cred->bp_task: 0x%lx, current: 0x%lx\n",
cred->bp_task, current);
if (mm && (cred->bp_pgd != swapper_pg_dir) && (cred->bp_pgd != mm->pgd ))
printk(KERN_ERR "[KDP] mm: 0x%lx, cred->bp_pgd: 0x%lx, swapper_pg_dir: %p, mm->pgd: 0x%lx\n",
mm, cred->bp_pgd, swapper_pg_dir, mm->pgd, cred->bp_pgd);
return ((cred->bp_task != current) ||
(mm && (!( in_interrupt() || in_softirq())) &&
(cred->bp_pgd != swapper_pg_dir) &&
(cred->bp_pgd != mm->pgd)));
}
static inline bool is_kdp_invalid_cred_sp(u64 cred, u64 sec_ptr)
{
struct task_security_struct *tsec = (struct task_security_struct *)sec_ptr;
u64 cred_size = sizeof(struct cred);
u64 tsec_size = sizeof(struct task_security_struct);
if((cred == (u64)&init_cred) && (sec_ptr == (u64)&init_sec))
return false;
if (!is_kdp_protect_addr(cred) ||
!is_kdp_protect_addr(cred + cred_size) ||
!is_kdp_protect_addr(sec_ptr) ||
!is_kdp_protect_addr(sec_ptr + tsec_size)) {
printk(KERN_ERR, "[KDP] cred: %d, cred + sizeof(cred): %d, sp: %d, sp + sizeof(tsec): %d",
is_kdp_protect_addr(cred),
is_kdp_protect_addr(cred + cred_size),
is_kdp_protect_addr(sec_ptr),
is_kdp_protect_addr(sec_ptr + tsec_size));
return true;
}
if ((u64)tsec->bp_cred != cred) {
printk(KERN_ERR, "[KDP] %s: tesc->bp_cred: %lx, cred: %lx\n",
__func__, (u64)tsec->bp_cred, cred);
return true;
}
return false;
}
inline int kdp_restrict_fork(struct filename *path)
{
struct cred *shellcred;
if (!strcmp(path->name, "/system/bin/patchoat") ||
!strcmp(path->name, "/system/bin/idmap2")) {
return 0;
}
if(KDP_IS_NONROOT(current)) {
shellcred = prepare_creds();
if (!shellcred)
return 1;
shellcred->uid.val = 2000;
shellcred->gid.val = 2000;
shellcred->euid.val = 2000;
shellcred->egid.val = 2000;
commit_creds(shellcred);
}
return 0;
}
/* This function is related Namespace */
#ifdef CONFIG_KDP_NS
static unsigned int cmp_ns_integrity(void)
{
struct mount *root = NULL;
struct nsproxy *nsp = NULL;
if (in_interrupt() || in_softirq())
return 0;
nsp = current->nsproxy;
if (!ns_protect || !nsp || !nsp->mnt_ns)
return 0;
root = current->nsproxy->mnt_ns->root;
if (root != root->mnt->bp_mount) {
printk(KERN_ERR "[KDP] NameSpace Mismatch %lx != %lx\n nsp: 0x%lx, mnt_ns: 0x%lx\n",
root, root->mnt->bp_mount, nsp, nsp->mnt_ns);
return 1;
}
return 0;
}
#endif // end CONFIG_KDP_NS
/* Main function to verify cred security context of a process */
int security_integrity_current(void)
{
const struct cred *cur_cred = current_cred();
rcu_read_lock();
if (kdp_enable &&
(is_kdp_invalid_cred_sp((u64)cur_cred, (u64)cur_cred->security)
|| cmp_sec_integrity(cur_cred, current->mm)
#ifdef CONFIG_KDP_NS
|| cmp_ns_integrity())) {
#else
)) {
#endif
rcu_read_unlock();
panic("KDP CRED PROTECTION VIOLATION\n");
}
rcu_read_unlock();
return 0;
}
#endif
#ifdef CONFIG_KDP_NS
/*------------------------------------------------
* Namespace
*------------------------------------------------*/
unsigned int ns_protect __kdp_ro = 0;
static int dex2oat_count = 0;
static DEFINE_SPINLOCK(mnt_vfsmnt_lock);
static struct super_block *rootfs_sb __kdp_ro = NULL;
static struct super_block *sys_sb __kdp_ro = NULL;
static struct super_block *odm_sb __kdp_ro = NULL;
static struct super_block *vendor_sb __kdp_ro = NULL;
static struct super_block *art_sb __kdp_ro = NULL;
static struct super_block *crypt_sb __kdp_ro = NULL;
static struct super_block *dex2oat_sb __kdp_ro = NULL;
static struct super_block *adbd_sb __kdp_ro = NULL;
static struct kmem_cache *vfsmnt_cache __read_mostly;
void cred_ctor_vfsmount(void *data)
{
/* Dummy constructor to make sure we have separate slabs caches. */
}
void __init kdp_mnt_init(void)
{
struct ns_param nsparam;
vfsmnt_cache = kmem_cache_create("vfsmnt_cache", sizeof(struct vfsmount),
0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, cred_ctor_vfsmount);
if (!vfsmnt_cache)
panic("Failed to allocate vfsmnt_cache \n");
memset((void *)&nsparam, 0, sizeof(struct ns_param));
nsparam.ns_buff_size = (u64)vfsmnt_cache->size;
nsparam.ns_size = (u64)sizeof(struct vfsmount);
nsparam.bp_offset = (u64)offsetof(struct vfsmount, bp_mount);
nsparam.sb_offset = (u64)offsetof(struct vfsmount, mnt_sb);
nsparam.flag_offset = (u64)offsetof(struct vfsmount, mnt_flags);
nsparam.data_offset = (u64)offsetof(struct vfsmount, data);
uh_call(UH_APP_KDP, NS_INIT, (u64)&nsparam, 0, 0, 0);
}
void __init kdp_init_mount_tree(struct vfsmount *mnt)
{
if (!rootfs_sb)
uh_call(UH_APP_KDP, SET_NS_SB_VFSMOUNT, (u64)&rootfs_sb, (u64)mnt, KDP_SB_ROOTFS, 0);
}
bool is_kdp_vfsmnt_cache(unsigned long addr)
{
static void *objp;
static struct kmem_cache *s;
static struct page *page;
objp = (void *)addr;
if (!objp)
return false;
page = virt_to_head_page(objp);
s = page->slab_cache;
if (s && s == vfsmnt_cache)
return true;
return false;
}
static int kdp_check_sb_mismatch(struct super_block *sb)
{
if (__is_kdp_recovery || __check_verifiedboot)
return 0;
if ((sb != rootfs_sb) && (sb != sys_sb) && (sb != odm_sb)
&& (sb != vendor_sb) && (sb != art_sb) && (sb != crypt_sb)
&& (sb != dex2oat_sb) && (sb != adbd_sb))
return 1;
return 0;
}
int invalid_drive(struct linux_binprm * bprm)
{
struct super_block *sb = NULL;
struct vfsmount *vfsmnt = NULL;
vfsmnt = bprm->file->f_path.mnt;
if (!vfsmnt || !is_kdp_vfsmnt_cache((unsigned long)vfsmnt)) {
printk(KERN_ERR "[KDP] Invalid Drive : %s, vfsmnt: 0x%lx\n",
bprm->filename, (unsigned long)vfsmnt);
return 1;
}
sb = vfsmnt->mnt_sb;
if (kdp_check_sb_mismatch(sb)) {
printk(KERN_ERR "[KDP] Superblock Mismatch -> %s vfsmnt: 0x%lx, mnt_sb: 0x%lx",
bprm->filename, (unsigned long)vfsmnt, (unsigned long)sb);
printk(KERN_ERR "[KDP] Superblock list : 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
(unsigned long)rootfs_sb, (unsigned long)sys_sb, (unsigned long)odm_sb,
(unsigned long)vendor_sb, (unsigned long)art_sb, (unsigned long)crypt_sb,
(unsigned long)dex2oat_sb, (unsigned long)adbd_sb);
return 1;
}
return 0;
}
#define KDP_CRED_SYS_ID 1000
int is_kdp_priv_task(void)
{
struct cred *cred = (struct cred *)current_cred();
if (cred->uid.val <= (uid_t)KDP_CRED_SYS_ID ||
cred->euid.val <= (uid_t)KDP_CRED_SYS_ID ||
cred->gid.val <= (gid_t)KDP_CRED_SYS_ID ||
cred->egid.val <= (gid_t)KDP_CRED_SYS_ID ) {
return 1;
}
return 0;
}
inline void kdp_set_mnt_root_sb(struct vfsmount *mnt, struct dentry *mnt_root, struct super_block *mnt_sb)
{
uh_call(UH_APP_KDP, SET_NS_ROOT_SB, (u64)mnt, (u64)mnt_root, (u64)mnt_sb, 0);
}
inline void kdp_assign_mnt_flags(struct vfsmount *mnt, int flags)
{
uh_call(UH_APP_KDP, SET_NS_FLAGS, (u64)mnt, (u64)flags, 0, 0);
}
inline void kdp_clear_mnt_flags(struct vfsmount *mnt, int flags)
{
int f = mnt->mnt_flags;
f &= ~flags;
kdp_assign_mnt_flags(mnt, f);
}
inline void kdp_set_mnt_flags(struct vfsmount *mnt, int flags)
{
int f = mnt->mnt_flags;
f |= flags;
kdp_assign_mnt_flags(mnt, f);
}
void kdp_set_ns_data(struct vfsmount *mnt, void *data)
{
uh_call(UH_APP_KDP, SET_NS_DATA, (u64)mnt, (u64)data, 0, 0);
}
int kdp_mnt_alloc_vfsmount(struct mount *mnt)
{
struct vfsmount *vfsmnt = NULL;
vfsmnt = kmem_cache_alloc(vfsmnt_cache, GFP_KERNEL);
if (!vfsmnt)
return 1;
spin_lock(&mnt_vfsmnt_lock);
uh_call(UH_APP_KDP, SET_NS_BP, (u64)vfsmnt, (u64)mnt, 0, 0);
mnt->mnt = vfsmnt;
spin_unlock(&mnt_vfsmnt_lock);
return 0;
}
void kdp_free_vfsmount(void *objp)
{
kmem_cache_free(vfsmnt_cache, objp);
}
static void kdp_populate_sb(char *mount_point, struct vfsmount *mnt)
{
if (!mount_point || !mnt)
return;
if (!odm_sb && !strncmp(mount_point, KDP_MOUNT_PRODUCT, KDP_MOUNT_PRODUCT_LEN))
uh_call(UH_APP_KDP, SET_NS_SB_VFSMOUNT, (u64)&odm_sb, (u64)mnt, KDP_SB_ODM, 0);
else if (!sys_sb && !strncmp(mount_point, KDP_MOUNT_SYSTEM, KDP_MOUNT_SYSTEM_LEN))
uh_call(UH_APP_KDP, SET_NS_SB_VFSMOUNT, (u64)&sys_sb, (u64)mnt, KDP_SB_SYS, 0);
else if (!vendor_sb && !strncmp(mount_point, KDP_MOUNT_VENDOR, KDP_MOUNT_VENDOR_LEN))
uh_call(UH_APP_KDP, SET_NS_SB_VFSMOUNT, (u64)&vendor_sb, (u64)mnt, KDP_SB_VENDOR, 0);
else if (!art_sb && !strncmp(mount_point, KDP_MOUNT_ART, KDP_MOUNT_ART_LEN - 1))
uh_call(UH_APP_KDP, SET_NS_SB_VFSMOUNT, (u64)&art_sb, (u64)mnt, KDP_SB_ART, 0);
else if (!crypt_sb && !strncmp(mount_point, KDP_MOUNT_CRYPT, KDP_MOUNT_CRYPT_LEN - 1))
uh_call(UH_APP_KDP, SET_NS_SB_VFSMOUNT, (u64)&crypt_sb, (u64)mnt, KDP_SB_CRYPT, 0);
else if (!dex2oat_sb && !strncmp(mount_point, KDP_MOUNT_DEX2OAT, KDP_MOUNT_DEX2OAT_LEN - 1))
uh_call(UH_APP_KDP, SET_NS_SB_VFSMOUNT, (u64)&dex2oat_sb, (u64)mnt, KDP_SB_DEX2OAT, 0);
else if (!dex2oat_count && !strncmp(mount_point, KDP_MOUNT_DEX2OAT, KDP_MOUNT_DEX2OAT_LEN)) {
uh_call(UH_APP_KDP, SET_NS_SB_VFSMOUNT, (u64)&dex2oat_sb, (u64)mnt, KDP_SB_DEX2OAT, 0);
dex2oat_count++;
}
else if (!adbd_sb && !strncmp(mount_point, KDP_MOUNT_ADBD, KDP_MOUNT_ADBD_LEN - 1))
uh_call(UH_APP_KDP, SET_NS_SB_VFSMOUNT, (u64)&adbd_sb, (u64)mnt, KDP_SB_ADBD, 0);
}
int kdp_do_new_mount(struct vfsmount *mnt, struct path *path)
{
char *buf = NULL;
char *dir_name;
buf = kzalloc(PATH_MAX, GFP_KERNEL);
if (!buf)
return -ENOMEM;
dir_name = dentry_path_raw(path->dentry, buf, PATH_MAX);
if (!sys_sb || !odm_sb || !vendor_sb || !art_sb || !crypt_sb || !dex2oat_sb || !dex2oat_count || !adbd_sb)
kdp_populate_sb(dir_name, mnt);
kfree(buf);
return 0;
}
#endif

View file

@ -1,387 +0,0 @@
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/uh.h>
#include <linux/nsproxy.h>
#include <linux/security.h>
#include <../../fs/mount.h>
#include <linux/sched/signal.h>
#include <linux/kdp.h>
/* Never enable this flag*/
//#define CONFIG_KDP_SEC_TEST
struct task_security_struct {
u32 osid; /* SID prior to last execve */
u32 sid; /* current SID */
u32 exec_sid; /* exec SID */
u32 create_sid; /* fscreate SID */
u32 keycreate_sid; /* keycreate SID */
u32 sockcreate_sid; /* fscreate SID */
void *bp_cred;
};
enum __KDP_TEST {
CMD_ID_CRED = 0,
CMD_ID_SEC_CONTEXT,
CMD_ID_NS,
};
#define KDP_PA_READ 0
#define KDP_PA_WRITE 1
/* BUF define */
#define KDP_BUF_SIZE 8192
#define KDP_LINE_MAX 80
static char kdp_test_buf[KDP_BUF_SIZE];
static unsigned long kdp_test_len = 0;
static DEFINE_RAW_SPINLOCK(par_lock);
static u64 *ha1;
static void kdp_print(const char *fmt, ...)
{
va_list aptr;
if (kdp_test_len > KDP_BUF_SIZE - KDP_LINE_MAX)
return;
va_start(aptr, fmt);
kdp_test_len += vsprintf(kdp_test_buf + kdp_test_len, fmt, aptr);
va_end(aptr);
}
static struct vfsmount *get_vfsmnt(struct task_struct *p)
{
if(!p || !(p->nsproxy) ||
!(p->nsproxy->mnt_ns) ||
!(p->nsproxy->mnt_ns->root))
return NULL;
return p->nsproxy->mnt_ns->root->mnt;
}
static bool hyp_check_page_ro(u64 va)
{
unsigned long flags;
u64 par = 0;
raw_spin_lock_irqsave(&par_lock, flags);
uh_call(UH_APP_KDP, TEST_GET_PAR, (unsigned long)va, KDP_PA_WRITE, 0, 0);
par = *ha1;
raw_spin_unlock_irqrestore(&par_lock, flags);
return (par & 0x1)? true: false;
}
static int test_case_kdp_ro(int cmd_id)
{
struct task_struct *p = NULL;
u64 ro = 0, rw = 0, dst;
for_each_process(p) {
switch(cmd_id) {
case CMD_ID_CRED:
/* Here dst points to struct cred */
dst = (u64)__task_cred(p);
break;
case CMD_ID_SEC_CONTEXT:
/* Here dst points to process security context */
dst = (u64)__task_cred(p)->security;
break;
case CMD_ID_NS:
/* Here dst points to process security context */
dst = (u64)get_vfsmnt(p);
break;
}
if(!dst)
continue;
if (hyp_check_page_ro(dst))
ro++;
else
rw++;
}
kdp_print("ro: %llu, rw: %llu\n", ro, rw);
return rw? 1: 0;
}
static int cred_match(struct task_struct *p, const struct cred *cred)
{
struct mm_struct *mm = p->mm;
pgd_t *tgt = NULL;
if(cred->bp_task != p) {
kdp_print("KDP_WARN task: #%s# cred: %p, task: %p bp_task: %p\n",
p->comm,cred, p,cred->bp_task);
return 0;
}
if(!(in_interrupt() || in_softirq()))
return 1;
tgt = mm? mm->pgd: init_mm.pgd;
if(cred->bp_pgd != tgt) {
kdp_print("KDP_WARN task: #%s# cred: %p, mm: %p, init_mm: %p, pgd: %p bp_pgd: %p \n",
p->comm,cred,mm,init_mm.pgd,tgt,cred->bp_pgd);
return 0;
}
return 1;
}
static int sec_context_match(const struct cred *cred)
{
struct task_security_struct *tsec = (struct task_security_struct *)cred->security;
if((u64)tsec->bp_cred != (u64)cred)
return 0;
return 1;
}
static int test_case_match_bp(int cmd_id)
{
struct task_struct *p = NULL;
u64 match = 0, mismatch = 0 , ret = 0;
for_each_process(p) {
switch(cmd_id) {
case CMD_ID_CRED:
/*Here dst points to struct cred*/
ret = cred_match(p,__task_cred(p));
break;
case CMD_ID_SEC_CONTEXT:
/*Here dst points to process security context*/
ret = sec_context_match(__task_cred(p));
break;
}
ret? match++: mismatch++;
}
kdp_print("match: %llu, mismatch: %llu\n", match, mismatch);
return mismatch? 1: 0;
}
static int test_case_cred_ro(void)
{
kdp_print("CRED PROTECTION ");
return test_case_kdp_ro(CMD_ID_CRED);
}
static int test_case_sec_context_ro(void)
{
kdp_print("SECURITY CONTEXT PROTECTION ");
return test_case_kdp_ro(CMD_ID_SEC_CONTEXT);
}
static int test_case_cred_match_bp(void)
{
kdp_print("CRED Back Poiner check ");
return test_case_match_bp(CMD_ID_CRED);
}
static int test_case_sec_context_match_bp(void)
{
kdp_print("Security Context Back Poiner check ");
return test_case_match_bp(CMD_ID_SEC_CONTEXT);
}
static int test_case_ns_ro(void)
{
kdp_print("NAMESPACE PROTECTION ");
return test_case_kdp_ro(CMD_ID_NS);
}
#ifdef CONFIG_KDP_SEC_TEST
enum {
CMD_ID_COMMIT_CRED,
CMD_ID_OVERRIDE_CRED,
CMD_ID_REVERT_CRED,
};
enum {
CMD_ID_SEC_WRITE_CRED,
CMD_ID_SEC_WRITE_SP,
CMD_ID_SEC_DIRECT_PE,
CMD_ID_SEC_INDIRECT_PE_COMMIT,
CMD_ID_SEC_INDIRECT_PE_OVERRIDE,
CMD_ID_SEC_INDIRECT_PE_REVERT,
};
#define PROCFS_MAX_SIZE 64
void write_ro(u64 *p)
{
memcpy(p,"c",1);
}
static void sec_test_cred(void)
{
write_ro((u64 *)current_cred());
}
static void sec_test_sp(void)
{
write_ro((u64 *)current_cred()->security);
}
struct cred *get_root_cred(void)
{
struct cred *cred;
cred = prepare_creds();
cred->uid.val = 0;
cred->gid.val = 0;
cred->euid.val = 0;
cred->egid.val = 0;
return cred;
}
static void sec_test_cred_direct_pe(void)
{
struct cred *rcred;
rcred = get_root_cred();
current->cred = rcred;
}
static void sec_test_cred_indirect_pe(int cmd_id)
{
struct cred *rcred;
rcred = get_root_cred();
printk("RKP_SEC_TEST #%d# BEFORE current cred uid = %llx euid = %llx gid = %llx egid = %llx Root Cred%llx\n",
cmd_id,current->cred->uid.val,current->cred->euid.val,current->cred->gid.val,current->cred->egid.val,(u64)rcred);
switch(cmd_id) {
case CMD_ID_COMMIT_CRED:
commit_creds(rcred);
break;
case CMD_ID_OVERRIDE_CRED:
override_creds(rcred);
break;
case CMD_ID_REVERT_CRED:
revert_creds(rcred);
break;
}
printk("RKP_SEC_TEST#%d# AFTER current cred uid = %llx euid = %llx gid = %llx egid = %llx Root Cred %llx\n",
cmd_id,current->cred->uid.val,current->cred->euid.val,current->cred->gid.val,current->cred->egid.val,(u64)rcred);
}
ssize_t kdp_write(struct file *filep, const char __user *buffer, size_t len, loff_t *offset)
{
char procfs_buffer[PROCFS_MAX_SIZE];
int buff_size;
int tcase;
buff_size = (len > PROCFS_MAX_SIZE)?PROCFS_MAX_SIZE:len;
if ( copy_from_user(procfs_buffer, buffer, buff_size) ) {
return -EFAULT;
}
sscanf(procfs_buffer,"%d",&tcase);
switch(tcase) {
case CMD_ID_SEC_WRITE_CRED:
sec_test_cred();
break;
case CMD_ID_SEC_WRITE_SP:
sec_test_sp();
break;
case CMD_ID_SEC_DIRECT_PE:
sec_test_cred_direct_pe();
break;
case CMD_ID_SEC_INDIRECT_PE_COMMIT:
sec_test_cred_indirect_pe(CMD_ID_COMMIT_CRED);
break;
case CMD_ID_SEC_INDIRECT_PE_OVERRIDE:
sec_test_cred_indirect_pe(CMD_ID_OVERRIDE_CRED);
break;
case CMD_ID_SEC_INDIRECT_PE_REVERT:
sec_test_cred_indirect_pe(CMD_ID_REVERT_CRED);
break;
}
return len;
}
#endif /* CONFIG_KDP_SEC_TEST*/
ssize_t kdp_read(struct file *filep, char __user *buffer, size_t count, loff_t *ppos)
{
int ret = 0, temp_ret = 0, i = 0;
struct test_case tc_funcs[] = {
{test_case_cred_ro, "TEST TASK_CRED_RO"},
{test_case_sec_context_ro, "TEST TASK_SECURITY_CONTEXT_RO"},
{test_case_cred_match_bp, "TEST CRED_MATCH_BACKPOINTERS"},
{test_case_sec_context_match_bp,"TEST TASK_SEC_CONTEXT_BACKPOINTER"},
{test_case_ns_ro, "TEST NAMESPACE_RO"},
};
int tc_num = sizeof(tc_funcs)/sizeof(struct test_case);
static bool done = false;
if (done)
return 0;
done = true;
for (i = 0; i < tc_num; i++) {
kdp_print( "KDP_TEST_CASE %d ===========> RUNNING %s\n", i, tc_funcs[i].describe);
temp_ret = tc_funcs[i].fn();
if (temp_ret) {
kdp_print("KDP_TEST_CASE %d ===========> %s FAILED WITH %d ERRORS\n",
i, tc_funcs[i].describe, temp_ret);
} else {
kdp_print("KDP_TEST_CASE %d ===========> %s PASSED\n", i, tc_funcs[i].describe);
}
ret += temp_ret;
}
if (ret)
kdp_print("KDP_TEST SUMMARY: FAILED WITH %d ERRORS\n", ret);
else
kdp_print("KDP_TEST SUMMARY: PASSED\n");
return simple_read_from_buffer(buffer, count, ppos, kdp_test_buf, kdp_test_len);
}
static const struct file_operations kdp_proc_fops = {
.read = kdp_read,
#ifdef CONFIG_KDP_SEC_TEST
.write = kdp_write,
#endif
};
static int __init kdp_test_init(void)
{
u64 va;
#ifndef CONFIG_KDP_SEC_TEST
if (proc_create("kdp_test", 0444, NULL, &kdp_proc_fops) == NULL) {
#else
if (proc_create("kdp_test", 0777, NULL, &kdp_proc_fops) == NULL) {
#endif
printk(KERN_ERR "KDP_TEST: Error creating proc entry");
return -1;
}
va = __get_free_page(GFP_KERNEL | __GFP_ZERO);
if (!va)
return -1;
uh_call(UH_APP_KDP, TEST_INIT, va, 0, 0, 0);
ha1 = (u64 *)va;
return 0;
}
static void __exit kdp_test_exit(void)
{
uh_call(UH_APP_KDP, TEST_EXIT, (u64)ha1, 0, 0, 0);
free_page((unsigned long)ha1);
remove_proc_entry("kdp_test", NULL);
}
module_init(kdp_test_init);
module_exit(kdp_test_exit);

View file

@ -1,80 +0,0 @@
#include <linux/rkp.h>
#include <linux/mm.h>
#include <asm/pgtable.h>
bool rkp_started __rkp_ro = false;
static u64 robuffer_base __rkp_ro = 0;
static u64 robuffer_size __rkp_ro = 0;
/* init/main.c */
void __init rkp_init(void)
{
struct rkp_init init_data;
memset((void *)&init_data, 0, sizeof(struct rkp_init));
/* initialized rkp_init struct */
init_data.magic = RKP_INIT_MAGIC;
init_data.vmalloc_start = VMALLOC_START;
init_data.no_fimc_verify = 1;
init_data.fimc_phys_addr = 0;
init_data._text = (u64)_text;
init_data._etext = (u64)_etext;
init_data._srodata = (u64)__start_rodata;
init_data._erodata = (u64)__end_rodata;
init_data.large_memory = 0;
init_data.vmalloc_end = (u64)high_memory;
init_data.init_mm_pgd = (u64)__pa(swapper_pg_dir);
init_data.id_map_pgd = (u64)__pa(idmap_pg_dir);
init_data.zero_pg_addr = (u64)__pa(empty_zero_page);
uh_call(UH_APP_RKP, RKP_START, (u64)&init_data, (u64)kimage_voffset, 0, 0);
rkp_started = true;
}
/* init/main.c */
void rkp_deferred_init(void)
{
uh_call(UH_APP_RKP, RKP_DEFERRED_START, 0, 0, 0, 0);
}
/* RO BUFFER */
void rkp_robuffer_init(void)
{
uh_call(UH_APP_RKP, RKP_GET_RO_INFO, (u64)&robuffer_base, (u64)&robuffer_size, 0, 0);
}
/* allocation */
inline phys_addr_t rkp_ro_alloc_phys(void)
{
phys_addr_t alloc_addr = 0;
uh_call(UH_APP_RKP, RKP_ROBUFFER_ALLOC, (u64)&alloc_addr, 1, 0, 0);
return alloc_addr;
}
inline void *rkp_ro_alloc(void)
{
void *addr = NULL;
uh_call(UH_APP_RKP, RKP_ROBUFFER_ALLOC, (u64)&addr, 1, 0, 0);
if (!addr)
return 0;
return (void *)__phys_to_virt(addr);
}
inline void rkp_ro_free(void *addr)
{
uh_call(UH_APP_RKP, RKP_ROBUFFER_FREE, (u64)addr, 0, 0, 0);
}
inline bool is_rkp_ro_buffer(u64 addr)
{
u64 pa = (u64)virt_to_phys((void *)addr);
if ((robuffer_base <= pa) && (pa < robuffer_base + robuffer_size))
return true;
else
return false;
}

View file

@ -1,517 +0,0 @@
#include <linux/module.h>
#include <linux/sched/signal.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/rkp.h>
/*
* BIT[0:1] TYPE PXN BIT
* 01 BLOCK 53 For LEVEL 0, 1, 2 //defined by L012_BLOCK_PXN
* 11 TABLE 59 For LEVEL 0, 1, 2 //defined by L012_TABLE_PXN
* 11 PAGE 53 For LEVEL 3 //defined by L3_PAGE_PXN
*/
#define L012_BLOCK_PXN (_AT(pmdval_t, 1) << 53)
#define L012_TABLE_PXN (_AT(pmdval_t, 1) << 59)
#define L3_PAGE_PXN (_AT(pmdval_t, 1) << 53)
#define MEM_END 0xfffffffffffff000 /* 4K aligned */
#define DESC_MASK 0xFFFFFFFFF000
#define RKP_PA_READ 0
#define RKP_PA_WRITE 1
/* BUF define */
#define RKP_BUF_SIZE 8192
#define RKP_LINE_MAX 80
static char rkp_test_buf[RKP_BUF_SIZE];
static unsigned long rkp_test_len = 0;
static unsigned long prot_user_l2 = 1;
static DEFINE_RAW_SPINLOCK(par_lock);
static u64 *ha1;
struct test_data {
u64 iter;
u64 pxn;
u64 no_pxn;
u64 read;
u64 write;
u64 cred_bkptr_match;
u64 cred_bkptr_mismatch;
};
static void buf_print(const char *fmt, ...)
{
va_list aptr;
if (rkp_test_len > RKP_BUF_SIZE - RKP_LINE_MAX) {
printk(KERN_ERR "RKP_TEST: Error Maximum buf");
return;
}
va_start(aptr, fmt);
rkp_test_len += vsprintf(rkp_test_buf+rkp_test_len, fmt, aptr);
va_end(aptr);
}
//if RO, return true; RW return false
static bool hyp_check_page_ro(u64 va)
{
unsigned long flags;
u64 par = 0;
raw_spin_lock_irqsave(&par_lock, flags);
uh_call(UH_APP_RKP, RKP_TEST_GET_PAR, (unsigned long)va, RKP_PA_WRITE, 0, 0);
par = *ha1;
raw_spin_unlock_irqrestore(&par_lock, flags);
return (par & 0x1) ? true : false;
}
static void hyp_check_l23pgt_rw(u64 *pg_l, unsigned int level, struct test_data *test)
{
unsigned int i;
// Level is 1 2
if (level >= 3)
return;
for (i = 0; i < 512; i++) {
if ((pg_l[i] & 3) == 3) {
test[level].iter++;
if (hyp_check_page_ro((u64)phys_to_virt(pg_l[i] & DESC_MASK)))
test[level].read++;
else
test[level].write++;
hyp_check_l23pgt_rw((u64 *) (phys_to_virt(pg_l[i] & DESC_MASK)), level + 1, test);
}
}
}
static pmd_t *get_addr_pmd(struct mm_struct *mm, unsigned long addr)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pgd = pgd_offset(mm, addr);
if (pgd_none(*pgd))
return NULL;
pud = pud_offset(pgd, addr);
if (pud_none(*pud))
return NULL;
pmd = pmd_offset(pud, addr);
if (pmd_none(*pmd))
return NULL;
return pmd;
}
static int test_case_user_pgtable_ro(void)
{
struct task_struct *task;
struct test_data test[3] = {{0}, {0}, {0} };
struct mm_struct *mm = NULL;
int i;
for_each_process(task) {
mm = task->active_mm;
if (!(mm) || !(mm->context.id.counter) || !(mm->pgd)) {
continue;
}
if (hyp_check_page_ro((u64)(mm->pgd)))
test[0].read++;
else
test[0].write++;
test[0].iter++;
hyp_check_l23pgt_rw(((u64 *) (mm->pgd)), 1, test);
}
for (i = 0; i < 3; i++) {
buf_print("\t\tL%d TOTAL PAGES %6llu | READ ONLY %6llu | WRITABLE %6llu\n",
i+1, test[i].iter, test[i].read, test[i].write);
}
//L1 and L2 pgtable should be RO
if ((!prot_user_l2) && (test[0].write == 0))
return 0;
if ((test[0].write == 0) && (test[1].write == 0))
return 0; //pass
else
return 1; //fail
}
static int test_case_kernel_pgtable_ro(void)
{
struct test_data test[3] = {{0}, {0}, {0} };
int i = 0;
// Check for swapper_pg_dir
test[0].iter++;
if (hyp_check_page_ro((u64)swapper_pg_dir))
test[0].read++;
else
test[0].write++;
hyp_check_l23pgt_rw((u64 *)swapper_pg_dir, 1, test);
for (i = 0; i < 3; i++)
buf_print("\t\tL%d TOTAL PAGE TABLES %6llu | READ ONLY %6llu |WRITABLE %6llu\n",
i+1, test[i].iter, test[i].read, test[i].write);
if ((test[0].write == 0) && (test[1].write == 0))
return 0;
else
return 1;
}
static int test_case_kernel_l3pgt_ro(void)
{
int rw = 0, ro = 0, i = 0;
u64 addrs[] = {
(u64)_text,
(u64)_etext
};
int len = sizeof(addrs)/sizeof(u64);
pmd_t * pmd;
u64 pgt_addr;
for (i = 0; i < len; i++) {
pmd = get_addr_pmd(&init_mm, addrs[i]);
pgt_addr = (u64)phys_to_virt(((u64)(pmd_val(*pmd))) & DESC_MASK);
if (hyp_check_page_ro(pgt_addr))
ro++;
else
rw++;
}
buf_print("\t\tKERNEL TEXT HEAD TAIL L3PGT | RO %6u | RW %6u\n", ro, rw);
return (rw == 0) ? 0 : 1;
}
// return true if addr mapped, otherwise return false
static bool page_pxn_set(unsigned long addr, u64 *xn, u64 *x)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
pgd = pgd_offset_k(addr);
if (pgd_none(*pgd))
return false;
pud = pud_offset(pgd, addr);
if (pud_none(*pud))
return false;
if (pud_sect(*pud)) {
if ((pud_val(*pud) & L012_BLOCK_PXN) > 0)
*xn += 1;
else
*x += 1;
return true;
}
pmd = pmd_offset(pud, addr);
if (pmd_none(*pmd))
return false;
if (pmd_sect(*pmd)) {
if ((pmd_val(*pmd) & L012_BLOCK_PXN) > 0)
*xn += 1;
else
*x += 1;
return true;
} else {
if ((pmd_val(*pmd) & L012_TABLE_PXN) > 0) {
*xn += 1;
return true;
}
}
// If pmd is table, such as kernel text head and tail, need to check L3
pte = pte_offset_kernel(pmd, addr);
if (pte_none(*pte))
return false;
if ((pte_val(*pte) & L3_PAGE_PXN) > 0)
*xn += 1;
else
*x += 1;
return true;
}
static void count_pxn(unsigned long pxn, int level, struct test_data *test)
{
test[level].iter++;
if (pxn)
test[level].pxn++;
else
test[level].no_pxn++;
}
static void walk_pte(pmd_t *pmd, int level, struct test_data *test)
{
pte_t *pte = pte_offset_kernel(pmd, 0UL);
unsigned i;
unsigned long prot;
for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
if (pte_none(*pte)) {
continue;
} else {
prot = pte_val(*pte) & L3_PAGE_PXN;
count_pxn(prot, level, test);
}
}
}
static void walk_pmd(pud_t *pud, int level, struct test_data *test)
{
pmd_t *pmd = pmd_offset(pud, 0UL);
unsigned i;
unsigned long prot;
for (i = 0; i < PTRS_PER_PMD; i++, pmd++) {
if (pmd_none(*pmd)) {
continue;
} else if (pmd_sect(*pmd)) {
prot = pmd_val(*pmd) & L012_BLOCK_PXN;
count_pxn(prot, level, test);
} else {
/*
* For user space, all L2 should have PXN, including block and
* table. Only kernel text head and tail L2 table can have no
* pxn, and kernel text middle L2 blocks can have no pxn
*/
BUG_ON(pmd_bad(*pmd));
prot = pmd_val(*pmd) & L012_TABLE_PXN;
count_pxn(prot, level, test);
walk_pte(pmd, level+1, test);
}
}
}
static void walk_pud(pgd_t *pgd, int level, struct test_data *test)
{
pud_t *pud = pud_offset(pgd, 0UL);
unsigned i;
for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
if (pud_none(*pud) || pud_sect(*pud)) {
continue;
} else {
BUG_ON(pud_bad(*pud));
walk_pmd(pud, level, test);
}
}
}
#define rkp_pgd_table (_AT(pgdval_t, 1) << 1)
#define rkp_pgd_bad(pgd) (!(pgd_val(pgd) & rkp_pgd_table))
static void walk_pgd(struct mm_struct *mm, int level, struct test_data *test)
{
pgd_t *pgd = pgd_offset(mm, 0UL);
unsigned i;
unsigned long prot;
for (i = 0; i < PTRS_PER_PGD; i++, pgd++) {
if (rkp_pgd_bad(*pgd)) {
continue;
} else { //table
prot = pgd_val(*pgd) & L012_TABLE_PXN;
count_pxn(prot, level, test);
walk_pud(pgd, level+1, test);
}
}
}
static int test_case_user_pxn(void)
{
struct task_struct *task = NULL;
struct mm_struct *mm = NULL;
struct test_data test[3] = {{0}, {0}, {0} };
int i = 0;
for_each_process(task) {
mm = task->active_mm;
if (!(mm) || !(mm->context.id.counter) || !(mm->pgd)) {
continue;
}
/* Check if PXN bit is set */
walk_pgd(mm, 0, test);
}
for (i = 0; i < 3; i++) {
buf_print("\t\tL%d TOTAL ENTRIES %6llu | PXN %6llu | NO_PXN %6llu\n",
i+1, test[i].iter, test[i].pxn, test[i].no_pxn);
}
//all 2nd level entries should be PXN
if (test[0].no_pxn == 0) {
prot_user_l2 = 0;
return 0;
} else if (test[1].no_pxn == 0) {
prot_user_l2 = 1;
return 0;
} else {
return 1;
}
}
struct mem_range {
u64 start_va;
u64 size; //in bytes
char *info;
bool no_rw;
bool no_x;
};
static int test_case_kernel_range_rwx(void)
{
int ret = 0;
u64 ro = 0, rw = 0;
u64 xn = 0, x = 0;
int i;
u64 j;
bool mapped = false;
u64 va_temp;
struct mem_range test_ranges[] = {
{(u64)VMALLOC_START, ((u64)_text) - ((u64)VMALLOC_START), "VMALLOC - STEXT", false, true},
{((u64)_text), ((u64)_etext) - ((u64)_text), "STEXT - ETEXT ", true, false},
{((u64)__start_rodata), ((u64) __end_rodata) - ((u64)__start_rodata), "SRODATA - ERODATA ", true, true},
{((u64)__end_rodata), MEM_END - ((u64) __end_rodata), "ERODATA -MEM_END", false, true},
};
int len = sizeof(test_ranges)/sizeof(struct mem_range);
buf_print("\t\t| MEMORY RANGES | %16s - %16s | %8s %8s %8s %8s\n",
"START", "END", "RO", "RW", "PXN", "PX");
for (i = 0; i < len; i++) {
for (j = 0; j < test_ranges[i].size/PAGE_SIZE; j++) {
va_temp = test_ranges[i].start_va + j*PAGE_SIZE;
mapped = page_pxn_set(va_temp, &xn, &x);
if (!mapped)
continue;
// only for mapped pages
if (hyp_check_page_ro(va_temp))
ro += 1;
else
rw += 1;
}
buf_print("\t\t|%s| %016llx - %016llx | %8llu %8llu %8llu %8llu\n",
test_ranges[i].info, test_ranges[i].start_va,
test_ranges[i].start_va + test_ranges[i].size,
ro, rw, xn, x);
if (test_ranges[i].no_rw && (rw != 0)) {
buf_print("RKP_TEST FAILED, NO RW PAGE ALLOWED, rw=%llu\n", rw);
ret++;
}
if (test_ranges[i].no_x && (x != 0)) {
buf_print("RKP_TEST FAILED, NO X PAGE ALLOWED, x=%llu\n", x);
ret++;
}
if ((rw != 0) && (x != 0)) {
buf_print("RKP_TEST FAILED, NO RWX PAGE ALLOWED, rw=%llu, x=%llu\n", rw, x);
ret++;
}
ro = 0; rw = 0;
xn = 0; x = 0;
}
return ret;
}
ssize_t rkp_read(struct file *filep, char __user *buffer, size_t count, loff_t *ppos)
{
int ret = 0, temp_ret = 0, i = 0;
struct test_case tc_funcs[] = {
{test_case_user_pxn, "TEST USER_PXN"},
{test_case_user_pgtable_ro, "TEST USER_PGTABLE_RO"},
{test_case_kernel_pgtable_ro, "TEST KERNEL_PGTABLE_RO"},
{test_case_kernel_l3pgt_ro, "TEST KERNEL TEXT HEAD TAIL L3PGT RO"},
{test_case_kernel_range_rwx, "TEST KERNEL_RANGE_RWX"},
};
int tc_num = sizeof(tc_funcs)/sizeof(struct test_case);
static bool done = false;
if (done)
return 0;
done = true;
if (!ha1) {
buf_print("ERROR RKP_TEST ha1 is NULL\n");
goto error;
}
for (i = 0; i < tc_num; i++) {
buf_print( "RKP_TEST_CASE %d ===========> RUNNING %s\n", i, tc_funcs[i].describe);
temp_ret = tc_funcs[i].fn();
if (temp_ret) {
buf_print("RKP_TEST_CASE %d ===========> %s FAILED WITH %d ERRORS\n",
i, tc_funcs[i].describe, temp_ret);
} else {
buf_print("RKP_TEST_CASE %d ===========> %s PASSED\n", i, tc_funcs[i].describe);
}
ret += temp_ret;
}
if (ret)
buf_print("RKP_TEST SUMMARY: FAILED WITH %d ERRORS\n", ret);
else
buf_print("RKP_TEST SUMMARY: PASSED\n");
error:
return simple_read_from_buffer(buffer, count, ppos, rkp_test_buf, rkp_test_len);
}
static const struct file_operations rkp_proc_fops = {
.read = rkp_read,
};
static int __init rkp_test_init(void)
{
u64 va;
if (proc_create("rkp_test", 0444, NULL, &rkp_proc_fops) == NULL) {
printk(KERN_ERR "RKP_TEST: Error creating proc entry");
return -1;
}
va = __get_free_page(GFP_KERNEL | __GFP_ZERO);
if (!va)
return -1;
uh_call(UH_APP_RKP, RKP_TEST_INIT, va, 0,0, 0);
ha1 = (u64 *)va;
return 0;
}
static void __exit rkp_test_exit(void)
{
uh_call(UH_APP_RKP, RKP_TEST_EXIT, (u64)ha1, 0, 0, 0);
free_page((unsigned long)ha1);
remove_proc_entry("rkp_test", NULL);
}
module_init(rkp_test_init);
module_exit(rkp_test_exit);

View file

@ -1,62 +0,0 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/highmem.h>
#include <linux/uh.h>
ssize_t uh_log_read(struct file *filep, char __user *buf, size_t size, loff_t *offset)
{
static size_t log_buf_size;
unsigned long *log_addr = 0;
if (!strcmp(filep->f_path.dentry->d_iname, "uh_log"))
log_addr = (unsigned long *)phys_to_virt(UH_LOG_START);
else
return -EINVAL;
if(!*offset) {
log_buf_size = 0;
while(log_buf_size < UH_LOG_SIZE && ((char *)log_addr)[log_buf_size] != 0)
log_buf_size++;
}
if (*offset >= log_buf_size)
return 0;
if (*offset + size > log_buf_size)
size = log_buf_size - *offset;
if (copy_to_user(buf, (const char *)log_addr + (*offset), size))
return -EFAULT;
*offset += size;
return size;
}
static const struct file_operations uh_proc_fops = {
.owner = THIS_MODULE,
.read = uh_log_read,
};
static int __init uh_log_init(void)
{
struct proc_dir_entry *entry;
entry = proc_create("uh_log", 0644, NULL, &uh_proc_fops);
if (!entry) {
pr_err("uh_log: Error creating proc entry\n");
return -ENOMEM;
}
pr_info("uh_log : create /proc/uh_log\n");
return 0;
}
static void __exit uh_log_exit(void)
{
remove_proc_entry("uh_log", NULL);
}
module_init(uh_log_init);
module_exit(uh_log_exit);

View file

@ -511,12 +511,6 @@ struct skb_shared_info {
* remains valid until skb destructor */
void * destructor_arg;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
uid_t uid;
pid_t pid;
u_int32_t knox_mark;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
/* must be last field, see pskb_expand_head() */
skb_frag_t frags[MAX_SKB_FRAGS];
};

View file

@ -1,162 +0,0 @@
/*
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
*
* Network Context Metadata Module[NCM]:Implementation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// KNOX NPA - START
#ifndef NCM_COMMON_H__
#define NCM_COMMON_H__
#define NCM_VERSION 11
#define INIT_UID_NAP 0
#define INIT_PID_NAP 1
#define DNS_PORT_NAP 53
#define IPV4_FAMILY_NAP 2
#define IPV6_FAMILY_NAP 10
#define INET6_ADDRSTRLEN_NAP 48
#define NCM_FLOW_TYPE_DEFAULT -1
#define NCM_FLOW_TYPE_ALL 0
#define NCM_FLOW_TYPE_OPEN 1
#define NCM_FLOW_TYPE_CLOSE 2
#define NCM_FLOW_TYPE_INTERMEDIATE 3
#include <linux/kernel.h>
#include <linux/inet.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <net/netfilter/nf_conntrack.h>
#define isIpv4AddressEqualsNull(srcaddr, dstaddr) ((((strcmp(srcaddr, "0.0.0.0")) || (strcmp(dstaddr, "0.0.0.0"))) == 0) ? 1 : 0)
#define isIpv6AddressEqualsNull(srcaddr, dstaddr) ((((strcmp(srcaddr, "0000:0000:0000:0000:0000:0000:0000:0000")) || (strcmp(dstaddr, "0000:0000:0000:0000:0000:0000:0000:0000"))) == 0) ? 1 : 0)
/* Struct Socket definition */
struct knox_socket_metadata {
/* The source port of the socket */
__u16 srcport;
/* The destination port of the socket */
__u16 dstport;
/* The Transport layer protocol of the socket*/
__u16 trans_proto;
/* The number of application layer bytes sent by the socket */
__u64 knox_sent;
/* The number of application layer bytes recieved by the socket */
__u64 knox_recv;
/* The uid which created the socket */
uid_t knox_uid;
/* The pid under which the socket was created */
pid_t knox_pid;
/* The parent user id under which the socket was created */
uid_t knox_puid;
/* The epoch time at which the socket was opened */
__u64 open_time;
/* The epoch time at which the socket was closed */
__u64 close_time;
/* The source address of the socket */
char srcaddr[INET6_ADDRSTRLEN_NAP];
/* The destination address of the socket */
char dstaddr[INET6_ADDRSTRLEN_NAP];
/* The name of the process which created the socket */
char process_name[PROCESS_NAME_LEN_NAP];
/* The name of the parent process which created the socket */
char parent_process_name[PROCESS_NAME_LEN_NAP];
/* The Domain name associated with the ip address of the socket. The size needs to be in sync with the userspace implementation */
char domain_name[DOMAIN_NAME_LEN_NAP];
/* The uid which originated the dns request */
uid_t knox_uid_dns;
/* The parent process id under which the socket was created */
pid_t knox_ppid;
/* The interface used by the flow to transmit packet */
char interface_name[IFNAMSIZ];
/* The flow type is used identify the current state of the network flow*/
int flow_type;
/* The struct defined is responsible for inserting the socket meta-data into kfifo */
struct work_struct work_kfifo;
};
/* Struct Socket definition */
struct knox_user_socket_metadata {
/* The source port of the socket */
__u16 srcport;
/* The destination port of the socket */
__u16 dstport;
/* The Transport layer protocol of the socket*/
__u16 trans_proto;
/* The number of application layer bytes sent by the socket */
__u64 knox_sent;
/* The number of application layer bytes recieved by the socket */
__u64 knox_recv;
/* The uid which created the socket */
uid_t knox_uid;
/* The pid under which the socket was created */
pid_t knox_pid;
/* The parent user id under which the socket was created */
uid_t knox_puid;
/* The epoch time at which the socket was opened */
__u64 open_time;
/* The epoch time at which the socket was closed */
__u64 close_time;
/* The source address of the socket */
char srcaddr[INET6_ADDRSTRLEN_NAP];
/* The destination address of the socket */
char dstaddr[INET6_ADDRSTRLEN_NAP];
/* The name of the process which created the socket */
char process_name[PROCESS_NAME_LEN_NAP];
/* The name of the parent process which created the socket */
char parent_process_name[PROCESS_NAME_LEN_NAP];
/* The Domain name associated with the ip address of the socket. The size needs to be in sync with the userspace implementation */
char domain_name[DOMAIN_NAME_LEN_NAP];
/* The uid which originated the dns request */
uid_t knox_uid_dns;
/* The parent process id under which the socket was created */
pid_t knox_ppid;
/* The interface used by the flow to transmit packet */
char interface_name[IFNAMSIZ];
/* The flow type is used identify the current state of the network flow*/
int flow_type;
};
/* The list of function which is being referenced */
extern unsigned int check_ncm_flag(void);
extern void knox_collect_conntrack_data(struct nf_conn *ct, int startStop, int where);
extern bool kfifo_status(void);
extern void insert_data_kfifo_kthread(struct knox_socket_metadata* knox_socket_metadata);
extern unsigned int check_intermediate_flag(void);
extern unsigned int get_intermediate_timeout(void);
/* Debug */
#define NCM_DEBUG 1
#if NCM_DEBUG
#define NCM_LOGD(...) printk("ncm: "__VA_ARGS__)
#else
#define NCM_LOGD(...)
#endif /* NCM_DEBUG */
#define NCM_LOGE(...) printk("ncm: "__VA_ARGS__)
/* IOCTL definitions*/
#define __NCMIOC 0x120
#define NCM_ACTIVATED_OPEN _IO(__NCMIOC, 2)
#define NCM_DEACTIVATED _IO(__NCMIOC, 4)
#define NCM_ACTIVATED_CLOSE _IO(__NCMIOC, 8)
#define NCM_ACTIVATED_ALL _IO(__NCMIOC, 16)
#define NCM_GETVERSION _IO(__NCMIOC, 32)
#define NCM_MATCH_VERSION _IO(__NCMIOC, 64)
#endif
// KNOX NPA - END

View file

@ -26,10 +26,6 @@
#include <net/netfilter/ipv6/nf_conntrack_icmpv6.h>
#include <net/netfilter/nf_conntrack_tuple.h>
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
#define PROCESS_NAME_LEN_NAP 128
#define DOMAIN_NAME_LEN_NAP 255
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
/* per conntrack: protocol private data */
union nf_conntrack_proto {
@ -107,39 +103,6 @@ struct nf_conn {
/* Storage reserved for other modules, must be the last member */
union nf_conntrack_proto proto;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
/* The number of application layer bytes sent by the socket */
__u64 knox_sent;
/* The number of application layer bytes recieved by the socket */
__u64 knox_recv;
/* The uid which created the socket */
uid_t knox_uid;
/* The pid under which the socket was created */
pid_t knox_pid;
/* The parent user id under which the socket was created */
uid_t knox_puid;
/* The epoch time at which the socket was opened */
__u64 open_time;
/* The name of the process which created the socket */
char process_name[PROCESS_NAME_LEN_NAP];
/* The name of the parent process which created the socket */
char parent_process_name[PROCESS_NAME_LEN_NAP];
/* The Domain name associated with the ip address of the socket. The size needs to be in sync with the userspace implementation */
char domain_name[DOMAIN_NAME_LEN_NAP];
/* The parent process id under which the socket was created */
pid_t knox_ppid;
/* The interface used by the flow to transmit packet */
char interface_name[IFNAMSIZ];
/* Atomic variable indicating start of flow */
atomic_t startFlow;
/* The value at which this ct is considered timed-out for intermediate flows */
/* Use 'u32 npa_timeout' if struct nf_conn->timeout is of type u32; Use 'struct timer_list npa_timeout' if struct nf_conn->timeout is of type struct timer_list;*/
u32 npa_timeout;
/* Atomic variable indicating end of intermediate flow */
atomic_t intermediateFlow;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
};
static inline struct nf_conn *

View file

@ -74,10 +74,6 @@
#include <net/smc.h>
#include <net/l3mdev.h>
#include <linux/android_kabi.h>
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
#define NAP_PROCESS_NAME_LEN 128
#define NAP_DOMAIN_NAME_LEN 255
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
/*
* This structure really needs to be cleaned up.
@ -500,18 +496,6 @@ struct sock {
#endif
struct sock_cgroup_data sk_cgrp_data;
struct mem_cgroup *sk_memcg;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
uid_t knox_uid;
pid_t knox_pid;
uid_t knox_dns_uid;
char domain_name[NAP_DOMAIN_NAME_LEN];
char process_name[NAP_PROCESS_NAME_LEN];
uid_t knox_puid;
pid_t knox_ppid;
char parent_process_name[NAP_PROCESS_NAME_LEN];
pid_t knox_dns_pid;
char dns_process_name[NAP_PROCESS_NAME_LEN];
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
void (*sk_state_change)(struct sock *sk);
void (*sk_data_ready)(struct sock *sk);
void (*sk_write_space)(struct sock *sk);

View file

@ -106,11 +106,6 @@
#define SO_PEERGROUPS 59
#define SO_ZEROCOPY 60
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
#define SO_SET_DOMAIN_NAME 1000
#define SO_SET_DNS_UID 1001
#define SO_SET_DNS_PID 1002
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
#define SO_TXTIME 61
#define SCM_TXTIME SO_TXTIME

View file

@ -87,15 +87,6 @@
#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */
#define TUN_F_UFO 0x10 /* I can handle UFO packets */
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
#define TUN_META_HDR 0x0020
#define TUNGETMETAPARAM _IOR('T', 218, int)
#define IFF_META_HDR 0x0004
#define TUN_GET_META_HDR_SZ 0
#define TUN_GET_META_MARK_OFFSET 1
#define DEFAULT_IHL 5
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */
#define TUN_PKT_STRIP 0x0001
struct tun_pi {

View file

@ -242,9 +242,6 @@ source "net/switchdev/Kconfig"
source "net/l3mdev/Kconfig"
source "net/qrtr/Kconfig"
source "net/ncsi/Kconfig"
# SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
source "net/ncm/Kconfig"
# SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
config RPS
bool

View file

@ -87,6 +87,3 @@ endif
obj-$(CONFIG_QRTR) += qrtr/
obj-$(CONFIG_NET_NCSI) += ncsi/
obj-$(CONFIG_XDP_SOCKETS) += xdp/
# SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
obj-$(CONFIG_KNOX_NCM) += ncm/
# SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }

View file

@ -142,11 +142,6 @@
#include <net/tcp.h>
#include <net/busy_poll.h>
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
#include <linux/sched.h>
#include <linux/pid.h>
#include <net/ncm.h>
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
static DEFINE_MUTEX(proto_list_mutex);
static LIST_HEAD(proto_list);
@ -622,94 +617,6 @@ out:
return ret;
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
/** The function sets the domain name associated with the socket. **/
static int sock_set_domain_name(struct sock *sk, char __user *optval,
int optlen)
{
int ret = -EADDRNOTAVAIL;
char domain[DOMAIN_NAME_LEN_NAP];
ret = -EINVAL;
if (optlen < 0)
goto out;
if (optlen > DOMAIN_NAME_LEN_NAP - 1)
optlen = DOMAIN_NAME_LEN_NAP - 1;
memset(domain, 0, sizeof(domain));
ret = -EFAULT;
if (copy_from_user(domain, optval, optlen))
goto out;
memcpy(sk->domain_name,domain, sizeof(sk->domain_name)-1);
ret = 0;
out:
return ret;
}
/** The function sets the uid associated with the dns socket. **/
static int sock_set_dns_uid(struct sock *sk, char __user *optval, int optlen)
{
int ret = -EADDRNOTAVAIL;
if (optlen < 0)
goto out;
if (optlen == sizeof(uid_t)) {
uid_t dns_uid;
ret = -EFAULT;
if (copy_from_user(&dns_uid, optval, sizeof(dns_uid)))
goto out;
memcpy(&sk->knox_dns_uid, &dns_uid, sizeof(sk->knox_dns_uid));
ret = 0;
}
out:
return ret;
}
/** The function sets the pid and the process name associated with the dns socket. **/
static int sock_set_dns_pid(struct sock *sk, char __user *optval, int optlen)
{
int ret = -EADDRNOTAVAIL;
struct pid *pid_struct = NULL;
struct task_struct *task = NULL;
int process_returnValue = -1;
char full_process_name[PROCESS_NAME_LEN_NAP] = {0};
if (optlen < 0)
goto out;
if (optlen == sizeof(pid_t)) {
pid_t dns_pid;
ret = -EFAULT;
if (copy_from_user(&dns_pid, optval, sizeof(dns_pid)))
goto out;
memcpy(&sk->knox_dns_pid, &dns_pid, sizeof(sk->knox_dns_pid));
if(check_ncm_flag()) {
pid_struct = find_get_pid(dns_pid);
if (pid_struct != NULL) {
task = pid_task(pid_struct,PIDTYPE_PID);
if (task != NULL) {
process_returnValue = get_cmdline(task, full_process_name, sizeof(full_process_name)-1);
if (process_returnValue > 0) {
memcpy(sk->dns_process_name, full_process_name, sizeof(sk->dns_process_name)-1);
} else {
memcpy(sk->dns_process_name, task->comm, sizeof(task->comm)-1);
}
}
}
}
ret = 0;
}
out:
return ret;
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
static inline void sock_valbool_flag(struct sock *sk, int bit, int valbool)
{
@ -759,14 +666,6 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
if (optname == SO_BINDTODEVICE)
return sock_setbindtodevice(sk, optval, optlen);
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
if (optname == SO_SET_DOMAIN_NAME)
return sock_set_domain_name(sk, optval, optlen);
if (optname == SO_SET_DNS_UID)
return sock_set_dns_uid(sk, optval, optlen);
if (optname == SO_SET_DNS_PID)
return sock_set_dns_pid(sk, optval, optlen);
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
if (optlen < sizeof(int))
return -EINVAL;
@ -1617,63 +1516,10 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
struct proto *prot, int kern)
{
struct sock *sk;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
struct pid *pid_struct = NULL;
struct task_struct *task = NULL;
int process_returnValue = -1;
char full_process_name[PROCESS_NAME_LEN_NAP] = {0};
struct pid *parent_pid_struct = NULL;
struct task_struct *parent_task = NULL;
int parent_returnValue = -1;
char full_parent_process_name[PROCESS_NAME_LEN_NAP] = {0};
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);
if (sk) {
sk->sk_family = family;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
/* assign values to members of sock structure when npa flag is present */
sk->knox_uid = current->cred->uid.val;
sk->knox_pid = current->tgid;
sk->knox_puid = 0;
sk->knox_ppid = 0;
sk->knox_dns_uid = 0;
sk->knox_dns_pid = 0;
memset(sk->process_name,'\0',sizeof(sk->process_name));
memset(sk->parent_process_name,'\0',sizeof(sk->parent_process_name));
memset(sk->dns_process_name,'\0',sizeof(sk->dns_process_name));
memset(sk->domain_name,'\0',sizeof(sk->domain_name));
if (check_ncm_flag()) {
pid_struct = find_get_pid(current->tgid);
if (pid_struct != NULL) {
task = pid_task(pid_struct, PIDTYPE_PID);
if (task != NULL) {
process_returnValue = get_cmdline(task, full_process_name, sizeof(full_process_name)-1);
if (process_returnValue > 0) {
memcpy(sk->process_name, full_process_name, sizeof(sk->process_name)-1);
} else {
memcpy(sk->process_name, task->comm, sizeof(task->comm)-1);
}
if (task->parent != NULL) {
parent_pid_struct = find_get_pid(task->parent->tgid);
if (parent_pid_struct != NULL) {
parent_task = pid_task(parent_pid_struct, PIDTYPE_PID);
if (parent_task != NULL) {
parent_returnValue = get_cmdline(parent_task, full_parent_process_name, sizeof(full_parent_process_name)-1);
if (parent_returnValue > 0) {
memcpy(sk->parent_process_name, full_parent_process_name, sizeof(sk->parent_process_name)-1);
} else {
memcpy(sk->parent_process_name, parent_task->comm, sizeof(parent_task->comm)-1);
}
sk->knox_puid = parent_task->cred->uid.val;
sk->knox_ppid = parent_task->tgid;
}
}
}
}
}
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
/*
* See comment in struct sock definition to understand
* why we need sk_prot_creator -acme

View file

@ -116,9 +116,6 @@
#include <net/sock_reuseport.h>
#include <net/addrconf.h>
#include <net/udp_tunnel.h>
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
#include <net/ncm.h>
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
struct udp_table udp_table __read_mostly;
EXPORT_SYMBOL(udp_table);
@ -2275,64 +2272,9 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
if (sk) {
struct dst_entry *dst = skb_dst(skb);
int ret;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
struct nf_conn *ct = NULL;
enum ip_conntrack_info ctinfo;
struct nf_conntrack_tuple *tuple = NULL;
char srcaddr[INET6_ADDRSTRLEN_NAP];
char dstaddr[INET6_ADDRSTRLEN_NAP];
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
if (unlikely(sk->sk_rx_dst != dst))
udp_sk_rx_dst_set(sk, dst);
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
/* function to handle open flows with incoming udp packets */
if (check_ncm_flag()) {
if ( (sk) && (sk->sk_protocol == IPPROTO_UDP) ) {
ct = nf_ct_get(skb, &ctinfo);
if ( (ct) && (!atomic_read(&ct->startFlow)) && (!nf_ct_is_dying(ct)) ) {
tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
if (tuple) {
sprintf(srcaddr,"%pI4",(void *)&tuple->src.u3.ip);
sprintf(dstaddr,"%pI4",(void *)&tuple->dst.u3.ip);
if ( !isIpv4AddressEqualsNull(srcaddr, dstaddr) ) {
atomic_set(&ct->startFlow, 1);
if ( check_intermediate_flag() ) {
/* Use 'atomic_set(&ct->intermediateFlow, 1); ct->npa_timeout = ((u32)(jiffies)) + (get_intermediate_timeout() * HZ);' if struct nf_conn->timeout is of type u32; */
ct->npa_timeout = ((u32)(jiffies)) + (get_intermediate_timeout() * HZ);
atomic_set(&ct->intermediateFlow, 1);
/* Use 'unsigned long timeout = ct->timeout.expires - jiffies;
if ( (timeout > 0) && ((timeout/HZ) > 5) ) {
atomic_set(&ct->intermediateFlow, 1);
ct->npa_timeout.expires = (jiffies) + (get_intermediate_timeout() * HZ);
add_timer(&ct->npa_timeout);
}'
if struct nf_conn->timeout is of type struct timer_list; */
}
ct->knox_uid = sk->knox_uid;
ct->knox_pid = sk->knox_pid;
memcpy(ct->process_name,sk->process_name,sizeof(ct->process_name)-1);
ct->knox_puid = sk->knox_puid;
ct->knox_ppid = sk->knox_ppid;
memcpy(ct->parent_process_name,sk->parent_process_name,sizeof(ct->parent_process_name)-1);
memcpy(ct->domain_name,sk->domain_name,sizeof(ct->domain_name)-1);
if ( (skb->dev) ) {
memcpy(ct->interface_name,skb->dev->name,sizeof(ct->interface_name)-1);
} else {
sprintf(ct->interface_name,"%s","null");
}
if ( (tuple != NULL) && (ntohs(tuple->dst.u.udp.port) == DNS_PORT_NAP) && (ct->knox_uid == INIT_UID_NAP) && (sk->knox_dns_uid > INIT_UID_NAP) ) {
ct->knox_puid = sk->knox_dns_uid;
ct->knox_ppid = sk->knox_dns_pid;
memcpy(ct->parent_process_name,sk->dns_process_name,sizeof(ct->parent_process_name)-1);
}
knox_collect_conntrack_data(ct, NCM_FLOW_TYPE_OPEN, 3);
}
}
}
}
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
ret = udp_unicast_rcv_skb(sk, skb, uh);
sock_put(sk);
@ -2344,62 +2286,8 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
saddr, daddr, udptable, proto);
sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
if (sk) {
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
struct nf_conn *ct = NULL;
enum ip_conntrack_info ctinfo;
struct nf_conntrack_tuple *tuple = NULL;
char srcaddr[INET6_ADDRSTRLEN_NAP];
char dstaddr[INET6_ADDRSTRLEN_NAP];
/* function to handle open flows with incoming udp packets */
if (check_ncm_flag()) {
if ( (sk) && (sk->sk_protocol == IPPROTO_UDP) ) {
ct = nf_ct_get(skb, &ctinfo);
if ( (ct) && (!atomic_read(&ct->startFlow)) && (!nf_ct_is_dying(ct)) ) {
tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
if (tuple) {
sprintf(srcaddr,"%pI4",(void *)&tuple->src.u3.ip);
sprintf(dstaddr,"%pI4",(void *)&tuple->dst.u3.ip);
if ( !isIpv4AddressEqualsNull(srcaddr, dstaddr) ) {
atomic_set(&ct->startFlow, 1);
if ( check_intermediate_flag() ) {
/* Use 'atomic_set(&ct->intermediateFlow, 1); ct->npa_timeout = ((u32)(jiffies)) + (get_intermediate_timeout() * HZ);' if struct nf_conn->timeout is of type u32; */
ct->npa_timeout = ((u32)(jiffies)) + (get_intermediate_timeout() * HZ);
atomic_set(&ct->intermediateFlow, 1);
/* Use 'unsigned long timeout = ct->timeout.expires - jiffies;
if ( (timeout > 0) && ((timeout/HZ) > 5) ) {
atomic_set(&ct->intermediateFlow, 1);
ct->npa_timeout.expires = (jiffies) + (get_intermediate_timeout() * HZ);
add_timer(&ct->npa_timeout);
}'
if struct nf_conn->timeout is of type struct timer_list; */
}
ct->knox_uid = sk->knox_uid;
ct->knox_pid = sk->knox_pid;
memcpy(ct->process_name,sk->process_name,sizeof(ct->process_name)-1);
ct->knox_puid = sk->knox_puid;
ct->knox_ppid = sk->knox_ppid;
memcpy(ct->parent_process_name,sk->parent_process_name,sizeof(ct->parent_process_name)-1);
memcpy(ct->domain_name,sk->domain_name,sizeof(ct->domain_name)-1);
if ( (skb->dev) ) {
memcpy(ct->interface_name,skb->dev->name,sizeof(ct->interface_name)-1);
} else {
sprintf(ct->interface_name,"%s","null");
}
if ( (tuple != NULL) && (ntohs(tuple->dst.u.udp.port) == DNS_PORT_NAP) && (ct->knox_uid == INIT_UID_NAP) && (sk->knox_dns_uid > INIT_UID_NAP) ) {
ct->knox_puid = sk->knox_dns_uid;
ct->knox_ppid = sk->knox_dns_pid;
memcpy(ct->parent_process_name,sk->dns_process_name,sizeof(ct->parent_process_name)-1);
}
knox_collect_conntrack_data(ct, NCM_FLOW_TYPE_OPEN, 4);
}
}
}
}
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
if (sk)
return udp_unicast_rcv_skb(sk, skb, uh);
}
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;

View file

@ -1,8 +0,0 @@
#
# Network Context Module Configuration
#
config KNOX_NCM
tristate "Network Context Module Support"
depends on NET
default y

View file

@ -1 +0,0 @@
obj-$(CONFIG_KNOX_NCM) := ncm.o

File diff suppressed because it is too large Load diff

View file

@ -205,7 +205,6 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o
obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o
obj-$(CONFIG_NETFILTER_XT_MATCH_TIME) += xt_time.o
obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o
obj-y += xt_domainfilter.o
# ipset
obj-$(CONFIG_IP_SET) += ipset/

View file

@ -58,9 +58,6 @@
#include <net/ip.h>
#include "nf_internals.h"
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
#include <net/ncm.h>
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
__cacheline_aligned_in_smp spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS];
EXPORT_SYMBOL_GPL(nf_conntrack_locks);
@ -472,13 +469,6 @@ clean_from_lists(struct nf_conn *ct)
static void nf_ct_add_to_dying_list(struct nf_conn *ct)
{
struct ct_pcpu *pcpu;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
/* Add 'del_timer(&ct->npa_timeout)' if struct nf_conn->timeout is of type struct timer_list; */
/* send dying conntrack entry to collect data */
if ( (check_ncm_flag()) && (ct != NULL) && (atomic_read(&ct->startFlow)) ) {
knox_collect_conntrack_data(ct, NCM_FLOW_TYPE_CLOSE, 10);
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
/* add this conntrack to the (per cpu) dying list */
ct->cpu = smp_processor_id();
@ -1240,15 +1230,7 @@ static void gc_worker(struct work_struct *work)
if (nf_ct_is_expired(tmp)) {
nf_ct_gc_expired(tmp);
continue;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
} else if ( (tmp != NULL) && (check_ncm_flag()) && (check_intermediate_flag()) && (atomic_read(&tmp->startFlow)) && (atomic_read(&tmp->intermediateFlow)) ) {
s32 npa_timeout = tmp->npa_timeout - ((u32)(jiffies));
if (npa_timeout <= 0) {
tmp->npa_timeout = ((u32)(jiffies)) + (get_intermediate_timeout() * HZ);
knox_collect_conntrack_data(tmp, NCM_FLOW_TYPE_INTERMEDIATE, 20);
}
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
if (nf_conntrack_max95 == 0 || gc_worker_skip_ct(tmp))
continue;
@ -1318,9 +1300,6 @@ __nf_conntrack_alloc(struct net *net,
gfp_t gfp, u32 hash)
{
struct nf_conn *ct;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
struct timespec open_timespec;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
/* We don't want any race condition at early drop stage */
atomic_inc(&net->ct.count);
@ -1345,28 +1324,6 @@ __nf_conntrack_alloc(struct net *net,
goto out;
spin_lock_init(&ct->lock);
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA {
/* initialize the conntrack structure members when memory is allocated */
if (ct != NULL) {
open_timespec = current_kernel_time();
ct->open_time = open_timespec.tv_sec;
ct->knox_uid = 0;
ct->knox_pid = 0;
memset(ct->process_name,'\0',sizeof(ct->process_name));
memset(ct->domain_name,'\0',sizeof(ct->domain_name));
ct->knox_puid = 0;
ct->knox_ppid = 0;
memset(ct->parent_process_name,'\0',sizeof(ct->parent_process_name));
ct->knox_sent = 0;
ct->knox_recv = 0;
memset(ct->interface_name,'\0',sizeof(ct->interface_name));
atomic_set(&ct->startFlow, 0);
/* Use 'ct->npa_timeout = 0' if struct nf_conn->timeout is of type u32;
Use 'setup_timer(&ct->npa_timeout, death_by_timeout_npa, (unsigned long)ct)' if struct nf_conn->timeout is of type struct timer_list; */
ct->npa_timeout = 0;
atomic_set(&ct->intermediateFlow, 0);
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_NPA }
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig;
ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.pprev = NULL;
ct->tuplehash[IP_CT_DIR_REPLY].tuple = *repl;

View file

@ -27,13 +27,6 @@
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_connmark.h>
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
#include <linux/types.h>
#include <linux/tcp.h>
#include <linux/ip.h>
#include <net/ip.h>
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
MODULE_AUTHOR("Henrik Nordstrom <hno@marasystems.com>");
MODULE_DESCRIPTION("Xtables: connection mark operations");
MODULE_LICENSE("GPL");
@ -42,64 +35,6 @@ MODULE_ALIAS("ip6t_CONNMARK");
MODULE_ALIAS("ipt_connmark");
MODULE_ALIAS("ip6t_connmark");
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
/* KNOX framework uses mark value 100 to 500
* when the special meta data is added
* This will indicate to the kernel code that
* it needs to append meta data to the packets
*/
#define META_MARK_BASE_LOWER 100
#define META_MARK_BASE_UPPER 500
/* Structure to hold metadata values
* intended for VPN clients to make
* more intelligent decisions
* when the KNOX meta mark
* feature is enabled
*/
struct knox_meta_param {
uid_t uid;
pid_t pid;
};
static unsigned int knoxvpn_uidpid(struct sk_buff *skb, u_int32_t newmark)
{
int szMetaData;
struct skb_shared_info *knox_shinfo = NULL;
szMetaData = sizeof(struct knox_meta_param);
if (skb != NULL) {
knox_shinfo = skb_shinfo(skb);
} else {
pr_err("KNOX: NULL SKB - no KNOX processing");
return -1;
}
if (skb->sk == NULL) {
pr_err("KNOX: skb->sk value is null");
return -1;
}
if (knox_shinfo == NULL) {
pr_err("KNOX: knox_shinfo is null");
return -1;
}
if (newmark < META_MARK_BASE_LOWER || newmark > META_MARK_BASE_UPPER) {
pr_err("KNOX: The mark is out of range");
return -1;
} else {
knox_shinfo->uid = skb->sk->knox_uid;
knox_shinfo->pid = skb->sk->knox_pid;
knox_shinfo->knox_mark = newmark;
}
return 0;
}
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
static unsigned int
connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info)
{
@ -149,9 +84,6 @@ connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info)
newmark = (skb->mark & ~info->nfmask) ^
new_targetmark;
skb->mark = newmark;
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN {
knoxvpn_uidpid(skb, newmark);
// SEC_PRODUCT_FEATURE_KNOX_SUPPORT_VPN }
break;
}
return XT_CONTINUE;

View file

@ -1,149 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd.
*
* Domain Filter Module:Implementation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/file.h>
#include <net/sock.h>
#include <net/inet_sock.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_domainfilter.h>
/*
* Check if a given string is the ending substring of another.
*/
bool endsWith(const char *host, const char *rule) {
size_t hostLen = strlen(host);
size_t ruleLen = strlen(rule);
if (hostLen >= ruleLen) {
unsigned int offSet = hostLen - ruleLen;
return strncmp(host + offSet , rule, ruleLen) == 0;
} else {
return false;
}
}
/*
* Check if a given string is the beginning substring of another.
*/
bool beginsWith(const char *host, const char *rule) {
size_t hostLen = strlen(host);
size_t ruleLen = strlen(rule);
if (hostLen >= ruleLen) {
return strncmp(host, rule, ruleLen) == 0;
} else {
return false;
}
}
/*
* Check if the given host matches the provided white/black list rules.
*/
bool matchHost(const char *rule, const char *host) {
size_t ruleLen = strlen(rule);
if (ruleLen == 1 && rule[0] == WILDCARD) { // rule is *, means all hosts
return true;
}
if (rule[0] == WILDCARD) { // starts with *
if (rule[ruleLen -1] == WILDCARD) { // also ends with *
// get the substring between the '*'s
char substrRule[XT_DOMAINFILTER_NAME_LEN];
strncpy(substrRule, rule+1, ruleLen-2);
substrRule[ruleLen-2] = '\0';
if(strstr(host, substrRule) != NULL) {
return true;
}
} else { // only starts with *
// remove * from beginning, so host must end if rule
char substrRule[XT_DOMAINFILTER_NAME_LEN];
strncpy(substrRule, rule+1, ruleLen-1);
substrRule[ruleLen-1] = '\0';
if (endsWith(host, substrRule))
return true;
}
} else if (rule[ruleLen -1] == WILDCARD) { // only ends with '*'
char substrRule[XT_DOMAINFILTER_NAME_LEN];
strncpy(substrRule, rule, ruleLen-1);
substrRule[ruleLen-1] = '\0';
if (beginsWith(host, substrRule))
return true;
} else if (strlen(host) == ruleLen &&
strcmp(host, rule) == 0) { // exact match
return true;
}
return false;
}
static int domainfilter_check(const struct xt_mtchk_param *par)
{
struct xt_domainfilter_match_info *info = par->matchinfo;
if (!(info->flags & (XT_DOMAINFILTER_WHITE|XT_DOMAINFILTER_BLACK))) {
return -EINVAL;
}
return 0;
}
static bool
domainfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_domainfilter_match_info *info = par->matchinfo;
struct sock *sk = skb_to_full_sk(skb);
if (sk == NULL) {
return false;
}
// check domain name match
if (sk->domain_name[0] != '\0') {
return matchHost(info->domain_name, sk->domain_name);
}
// didn't match
return false;
}
static struct xt_match domainfilter_mt_reg __read_mostly = {
.name = "domainfilter",
.revision = 1,
.family = NFPROTO_UNSPEC,
.checkentry = domainfilter_check,
.match = domainfilter_mt,
.matchsize = sizeof(struct xt_domainfilter_match_info),
.hooks = (1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_LOCAL_IN),
.me = THIS_MODULE,
};
static int __init domainfilter_mt_init(void)
{
return xt_register_match(&domainfilter_mt_reg);
}
static void __exit domainfilter_mt_exit(void)
{
xt_unregister_match(&domainfilter_mt_reg);
}
module_init(domainfilter_mt_init);
module_exit(domainfilter_mt_exit);
MODULE_AUTHOR("Antonio Junqueira <antonio.n@samsung.com>");
MODULE_DESCRIPTION("Xtables: domain name matching");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_domainfilter");
MODULE_ALIAS("ip6t_domainfilter");

View file

@ -231,7 +231,6 @@ config STATIC_USERMODEHELPER_PATH
specify an empty string here (i.e. "").
source security/selinux/Kconfig
source security/samsung/defex_lsm/Kconfig
source security/smack/Kconfig
source security/tomoyo/Kconfig
source security/apparmor/Kconfig
@ -239,7 +238,6 @@ source security/loadpin/Kconfig
source security/yama/Kconfig
source security/integrity/Kconfig
source "security/hdm/Kconfig"
choice
prompt "Default security module"
@ -280,9 +278,5 @@ config DEFAULT_SECURITY
source "security/Kconfig.hardening"
source "security/samsung/defex_lsm/Kconfig" # ADDED BY LEGO AUTOMATICALLY: DO NOT SUBMIT
source "security/samsung/proca/Kconfig" # ADDED BY LEGO AUTOMATICALLY: DO NOT SUBMIT
source "security/samsung/proca/gaf/Kconfig" # ADDED BY LEGO AUTOMATICALLY: DO NOT SUBMIT
source "security/samsung/five/Kconfig" # ADDED BY LEGO AUTOMATICALLY: DO NOT SUBMIT
endmenu

View file

@ -10,7 +10,6 @@ subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
subdir-$(CONFIG_SECURITY_YAMA) += yama
subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
subdir-$(CONFIG_SECURITY_DEFEX) += samsung/defex_lsm
# always enable default capabilities
obj-y += commoncap.o
@ -26,16 +25,8 @@ obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/
obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
obj-$(CONFIG_SECURITY_YAMA) += yama/
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
obj-$(CONFIG_SECURITY_DEFEX) += samsung/defex_lsm/built-in.a
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
# Object integrity file lists
subdir-$(CONFIG_INTEGRITY) += integrity
obj-$(CONFIG_INTEGRITY) += integrity/
# HDM driver
obj-$(CONFIG_HDM) += hdm/
obj-y += samsung/defex_lsm/ # ADDED BY LEGO AUTOMATICALLY: DO NOT SUBMIT
obj-y += samsung/proca/ # ADDED BY LEGO AUTOMATICALLY: DO NOT SUBMIT
obj-y += samsung/proca/gaf/ # ADDED BY LEGO AUTOMATICALLY: DO NOT SUBMIT
obj-y += samsung/five/ # ADDED BY LEGO AUTOMATICALLY: DO NOT SUBMIT
obj-$(CONFIG_INTEGRITY) += integrity/

View file

@ -1,20 +0,0 @@
# @file Kconfig
# @brief Kconfig for HDM driver
# Copyright (c) 2019, Samsung Electronics Corporation. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 and
# only version 2 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
config HDM
bool "HDM feature enable"
depends on !SEC_FACTORY
default n
help
This enables hdm driver

View file

@ -1,14 +0,0 @@
#@file Makefile
#@brief Makefile for HDM driver
#Copyright (c) 2019, Samsung Electronics Corporation. All rights reserved.
#
#This program is free software; you can redistribute it and/or modify
#it under the terms of the GNU General Public License version 2 and
#only version 2 as published by the Free Software Foundation.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
obj-$(CONFIG_HDM) += hdm.o

View file

@ -1,186 +0,0 @@
/*
* @file hdm.c
* @brief HDM Support
* Copyright (c) 2020, Samsung Electronics Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
#if defined(CONFIG_ARCH_QCOM)
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/resource.h>
#endif
#include <linux/hdm.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/uh.h>
#include <linux/sec_class.h>
#if defined(CONFIG_ARCH_QCOM)
#include <linux/sched/signal.h>
#include <soc/qcom/secure_buffer.h>
#include <soc/qcom/qtee_shmbridge.h>
#endif
#include "hdm_log.h"
int hdm_log_level = HDM_LOG_LEVEL;
void hdm_printk(int level, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
if (hdm_log_level < level)
return;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
printk(KERN_INFO "%s %pV", TAG, &vaf);
va_end(args);
}
static ssize_t store_hdm_policy(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long mode = HDM_CMD_MAX;
int c, p;
if (count == 0) {
hdm_err("%s count = 0\n", __func__);
goto error;
}
if (kstrtoul(buf, 0, &mode)) {
goto error;
};
if (mode > HDM_CMD_MAX) {
hdm_err("%s command size max fail. %d\n", __func__, mode);
goto error;
}
hdm_info("%s: command id: 0x%x\n", __func__, (int)mode);
c = (int)(mode & HDM_C_BITMASK);
p = (int)(mode & HDM_P_BITMASK);
hdm_info("%s m:0x%x c:0x%x p:0x%x\n", __func__, (int)mode, c, p);
switch (c) {
#if defined(CONFIG_ARCH_QCOM)
case HDM_HYP_CALL:
hdm_info("%s HDM_HYP_CALL\n", __func__);
uh_call(UH_APP_HDM, 9, 0, p, 0, 0);
break;
case HDM_HYP_CALLP:
hdm_info("%s HDM_HYP_CALLP\n", __func__);
uh_call(UH_APP_HDM, 2, 0, p, 0, 0);
break;
#endif
default:
goto error;
}
error:
return count;
}
static DEVICE_ATTR(hdm_policy, 0220, NULL, store_hdm_policy);
#if defined(CONFIG_ARCH_QCOM)
static uint64_t qseelog_shmbridge_handle;
static int __init __hdm_init_of(void)
{
struct device_node *node;
struct resource r;
int ret;
phys_addr_t addr;
u64 size;
uint32_t ns_vmids[] = {VMID_HLOS};
uint32_t ns_vm_perms[] = {PERM_READ | PERM_WRITE};
uint32_t ns_vm_nums = 1;
int src_vmids[1] = {VMID_HLOS};
int dest_vmids[1] = {VMID_CP_BITSTREAM};
int dest_perms[1] = {PERM_READ | PERM_WRITE};
hdm_info("%s start\n", __func__);
node = of_find_node_by_name(NULL, "samsung,sec_hdm");
if (!node) {
hdm_err("failed of_find_node_by_name\n");
return -ENODEV;
}
node = of_parse_phandle(node, "memory-region", 0);
if (!node) {
hdm_err("no memory-region specified\n");
return -EINVAL;
}
ret = of_address_to_resource(node, 0, &r);
if (ret) {
hdm_err("failed of_address_to_resource\n");
return ret;
}
addr = r.start;
size = resource_size(&r);
ret = qtee_shmbridge_register(addr, size, ns_vmids, ns_vm_perms,
ns_vm_nums, PERM_READ | PERM_WRITE, &qseelog_shmbridge_handle);
if (ret)
hdm_err("failed to create bridge for qsee_log buffer\n");
ret = hyp_assign_phys(addr, size, src_vmids, 1, dest_vmids, dest_perms, 1);
if (ret) {
hdm_err("%s: failed for %pa address of size %zx rc:%d\n",
__func__, &addr, size, ret);
}
hdm_info("%s done\n", __func__);
return 0;
}
#endif
static int __init hdm_test_init(void)
{
struct device *dev;
#if defined(CONFIG_ARCH_QCOM)
int err;
#endif
dev = sec_device_create(NULL, "hdm");
WARN_ON(!dev);
if (IS_ERR(dev))
hdm_err("%s Failed to create devce\n", __func__);
if (device_create_file(dev, &dev_attr_hdm_policy) < 0)
hdm_err("%s Failed to create device file\n", __func__);
#if defined(CONFIG_ARCH_QCOM)
err = __hdm_init_of();
if (err)
return err;
#endif
hdm_info("%s end\n", __func__);
return 0;
}
module_init(hdm_test_init);

View file

@ -1,38 +0,0 @@
/*
* @file hdm_log.h
* @brief Header file for HDM log
* Copyright (c) 2019, Samsung Electronics Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __HDM_LOG_H__
#define __HDM_LOG_H__
#include <linux/printk.h>
#define TAG "[sec_hdm]"
void hdm_printk(int level, const char *fmt, ...);
#define DEV_ERR (1)
#define DEV_WARN (2)
#define DEV_NOTI (3)
#define DEV_INFO (4)
#define DEV_DEBUG (5)
#define HDM_LOG_LEVEL DEV_INFO
#define hdm_err(fmt, ...) hdm_printk(DEV_ERR, fmt, ## __VA_ARGS__)
#define hdm_warn(fmt, ...) hdm_printk(DEV_WARN, fmt, ## __VA_ARGS__)
#define hdm_noti(fmt, ...) hdm_printk(DEV_NOTI, fmt, ## __VA_ARGS__)
#define hdm_info(fmt, ...) hdm_printk(DEV_INFO, fmt, ## __VA_ARGS__)
#define hdm_debug(fmt, ...) hdm_printk(DEV_DEBUG, fmt, ## __VA_ARGS__)
#endif //__HDM_LOG_H__

View file

@ -1,31 +0,0 @@
config SECURITY_DEFEX
bool "Defex Support"
depends on SECURITY && (!KCOV || SAMSUNG_PRODUCT_SHIP)
select CRYPTO
select CRYPTO_RSA
select CRYPTO_SHA1
select CRYPTO_SHA256
select CRYPTO_HASH_INFO
select INTEGRITY_SIGNATURE
select INTEGRITY_ASYMMETRIC_KEYS
default n
help
This selects the Defex support.
If you are unsure how to answer this question, answer N.
config DEFEX_KERNEL_ONLY
bool "Defex Kernel Only"
depends on SECURITY_DEFEX
default n
help
This lets defex know whether kernel-only build or not.
Default value will be set to "y" if the build is kernel-only.
And it will be changed to "n" by build.py at platform-build.
config SECURITY_DEFEX_USER
bool "Defex USER build"
depends on SECURITY_DEFEX
default n
help
This lets defex to know whether USER build performed or not
in case of TARGET_BUILD_VARIANT variable is empty.

View file

@ -1,237 +0,0 @@
#
# Makefile for the Defex
#
# Features to Enable
PED_ENABLE=true
SAFEPLACE_ENABLE=true
IMMUTABLE_ENABLE=true
LP_ENABLE=true
UMH_RESTRICTION_ENABLE=true
TRUSTED_MAP_ENABLE=false
USER_BUILD=false
# Additional debug
LOG_BUFFER_ENABLE=false
SHOW_RULES_ENABLE=false
ifeq (,$(TARGET_BUILD_VARIANT))
ifeq ($(CONFIG_SECURITY_DEFEX_USER),y)
USER_BUILD := true
endif
else
ifeq ($(TARGET_BUILD_VARIANT),user)
USER_BUILD := true
endif
endif
ifneq ($(wildcard $(srctree)/include/crypto/internal/rsa.h),)
$(warning [DEFEX] INTEGRITY_ENABLE)
INTEGRITY_ENABLE=true
endif
# caches to enable
CACHES_ENABLE=true
# OEM Unlock dependency
OEM_UNLOCK_DEPENDENCY=true
# use the ramdisk or system_root to store rules file
RAMDISK_ENABLE=true
# do signing for rules
SIGN_ENABLE=true
defex-y := core/defex_common.o
defex-y += core/defex_lsm.o
defex-y += core/defex_main.o
defex-y += core/defex_get_mode.o
defex-y += core/defex_rules_proc.o
defex-y += core/defex_tailer.o
defex-y += catch_engine/defex_catch_list.o
defex-y += catch_engine/defex_ht.o
defex-y += defex_rules.o
defex-$(CONFIG_COMPAT) += catch_engine/defex_catch_list_compat.o
# Immutable Feature is applied with permissive mode first.
DEFEX_DEFINES := -DDEFEX_PERMISSIVE_IM
# Integrity Feature is applied with permissive mode first.
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_INT
ifeq ($(CONFIG_DEFEX_KERNEL_ONLY), y)
DEFEX_DEFINES += -DDEFEX_KERNEL_ONLY
ifeq ($(CONFIG_SAMSUNG_PRODUCT_SHIP), y)
$(warning [DEFEX] Kernel_only & Ship)
else
$(warning [DEFEX] Kernel_only & Noship)
defex-y += debug/defex_debug.o
defex-y += core/defex_sysfs.o
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_INT
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_SP
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_TM
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_IM
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_LP
DEFEX_DEFINES += -DDEFEX_DEBUG_ENABLE
ifeq ($(LOG_BUFFER_ENABLE), true)
DEFEX_DEFINES += -DDEFEX_LOG_BUFFER_ENABLE
endif
ifeq ($(SHOW_RULES_ENABLE), true)
defex-y += debug/defex_rules_show.o
DEFEX_DEFINES += -DDEFEX_SHOW_RULES_ENABLE
endif
endif
endif
ifeq ($(CONFIG_SEC_FACTORY), y)
DEFEX_DEFINES += -DDEFEX_FACTORY_ENABLE
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_LP
endif
ifeq ($(PED_ENABLE), true)
defex-y += feature_privilege_escalation_detection/defex_priv.o
DEFEX_DEFINES += -DDEFEX_PED_ENABLE
endif
ifeq ($(SAFEPLACE_ENABLE), true)
defex-y += feature_safeplace/defex_safeplace.o
DEFEX_DEFINES += -DDEFEX_SAFEPLACE_ENABLE
endif
ifeq ($(INTEGRITY_ENABLE), true)
defex-y += feature_safeplace/defex_integrity.o
DEFEX_DEFINES += -DDEFEX_INTEGRITY_ENABLE
endif
ifeq ($(IMMUTABLE_ENABLE), true)
defex-y += feature_immutable/defex_immutable.o
DEFEX_DEFINES += -DDEFEX_IMMUTABLE_ENABLE
endif
ifeq ($(LP_ENABLE), true)
DEFEX_DEFINES += -DDEFEX_LP_ENABLE
endif
ifeq ($(UMH_RESTRICTION_ENABLE), true)
DEFEX_DEFINES += -DDEFEX_UMH_RESTRICTION_ENABLE
endif
ifeq ($(CACHES_ENABLE), true)
defex-y += catch_engine/defex_caches.o
DEFEX_DEFINES += -DDEFEX_CACHES_ENABLE
endif
ifeq ($(OEM_UNLOCK_DEPENDENCY), true)
DEFEX_DEFINES += -DDEFEX_DEPENDING_ON_OEMUNLOCK
endif
ifeq ($(RAMDISK_ENABLE), true)
DEFEX_DEFINES += -DDEFEX_RAMDISK_ENABLE
ifeq ($(SIGN_ENABLE), true)
defex-y += cert/defex_cert.o
defex-y += cert/defex_sign.o
DEFEX_DEFINES += -DDEFEX_SIGN_ENABLE
endif
endif
ifeq ($(TRUSTED_MAP_ENABLE), true)
DEFEX_DEFINES += -DDEFEX_TRUSTED_MAP_ENABLE
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_TM
#DEFEX_DEFINES += -DDEFEX_TM_DEFAULT_POLICY_ENABLE
defex-y += feature_trusted_map/defex_trusted_map.o
defex-y += feature_trusted_map/dtm.o
defex-y += feature_trusted_map/dtm_engine.o
defex-y += feature_trusted_map/dtm_log.o
defex-y += feature_trusted_map/dtm_utils.o
defex-y += feature_trusted_map/ptree.o
endif
ifeq ($(USER_BUILD), true)
$(warning [DEFEX] DEBUG_DISABLE)
ifeq ($(CONFIG_SECURITY_DSMS), y)
DEFEX_DEFINES += -DDEFEX_DSMS_ENABLE
endif
else
$(warning [DEFEX] DEBUG_ENABLE)
defex-y += debug/defex_debug.o
defex-y += core/defex_sysfs.o
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_INT
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_SP
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_TM
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_IM
DEFEX_DEFINES += -DDEFEX_PERMISSIVE_LP
DEFEX_DEFINES += -DDEFEX_DEBUG_ENABLE
ifeq ($(LOG_BUFFER_ENABLE), true)
DEFEX_DEFINES += -DDEFEX_LOG_BUFFER_ENABLE
endif
ifeq ($(SHOW_RULES_ENABLE), true)
defex-y += debug/defex_rules_show.o
DEFEX_DEFINES += -DDEFEX_SHOW_RULES_ENABLE
endif
endif
# kunit tests options:
ifeq ($(CONFIG_SEC_KUNIT), y)
GCOV_PROFILE := y
DEFEX_DEFINES += -DDEFEX_KUNIT_ENABLED
else
DEFEX_DEFINES += -D__visible_for_testing=static
endif
ccflags-y := -Wformat
EXTRA_CFLAGS += -I$(srctree)/$(src)
EXTRA_AFLAGS += -Isecurity/samsung/defex_lsm
EXTRA_CFLAGS += -I$(srctree)/$(src)/cert
EXTRA_AFLAGS += -Isecurity/samsung/defex_lsm/cert
ifneq ($(wildcard $(srctree)/$(src)/pack_rules.c),)
EXTRA_CFLAGS += $(DEFEX_DEFINES)
EXTRA_AFLAGS += $(DEFEX_DEFINES)
hostprogs := pack_rules
hostprogs-y := pack_rules
HOST_EXTRACFLAGS += $(DEFEX_DEFINES)
clean-files := $(obj)/defex_packed_rules.inc
DEPEND_LIST := $(obj)/pack_rules
quiet_cmd_pack = PACK $<
cmd_pack = $(obj)/pack_rules -p $< $@ $(srctree)/$(src)/defex_packed_rules.bin
quiet_cmd_mkey = MAKEKEY $<
cmd_mkey = cp -n $< $@ 2>/dev/null || true
$(obj)/core/defex_rules_proc.o: $(obj)/pack_rules $(obj)/defex_packed_rules.inc
$(obj)/cert/defex_cert.o: $(obj)/cert/pubkey_eng.der $(obj)/cert/pubkey_user.der
$(obj)/cert/pubkey_eng.der: $(srctree)/$(src)/cert/x509_five_eng.der
$(call cmd,mkey)
$(obj)/cert/pubkey_user.der: $(srctree)/$(src)/cert/x509_five_user.der
$(call cmd,mkey)
SOURCE_RULES := $(srctree)/$(src)/defex_rules.c
ifneq ($(wildcard $(srctree)/$(src)/file_list),)
$(warning '[DEFEX] file_list found')
SOURCE_RULES := $(srctree)/$(src)/defex_rules_reduced.c
DEPEND_LIST += $(SOURCE_RULES)
DEPEND_LIST += $(srctree)/$(src)/file_list
clean-files += $(DEPEND_LIST)
quiet_cmd_reduce = REDUCE $<
cmd_reduce = $(obj)/pack_rules -r $< $@ $(srctree)/$(src)/file_list
$(srctree)/$(src)/defex_rules_reduced.c: $(srctree)/$(src)/defex_rules.c $(obj)/pack_rules
$(call cmd,reduce)
endif
$(obj)/defex_packed_rules.inc: $(SOURCE_RULES) $(DEPEND_LIST)
$(call cmd,pack)
@cp -n $(obj)/pack_rules $(srctree)/$(src)/pack_rules 2>/dev/null || true
else
EXTRA_CFLAGS += $(DEFEX_DEFINES)
EXTRA_AFLAGS += $(DEFEX_DEFINES)
endif
obj-$(CONFIG_SECURITY_DEFEX) := $(defex-y)

View file

@ -1,177 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/file.h>
#include <linux/types.h>
#include <linux/unistd.h>
#include <linux/spinlock.h>
#include "include/defex_caches.h"
__visible_for_testing struct defex_file_cache_list file_cache;
DEFINE_SPINLOCK(defex_caches_lock);
void defex_file_cache_init(void)
{
int i;
struct defex_file_cache_entry *current_entry;
unsigned long flags;
spin_lock_irqsave(&defex_caches_lock, flags);
for (i = 0; i < FILE_CACHE_SIZE; i++) {
current_entry = &file_cache.entry[i];
current_entry->next_entry = i + 1;
current_entry->prev_entry = i - 1;
current_entry->pid = -1;
current_entry->file_addr = NULL;
}
file_cache.first_entry = 0;
file_cache.last_entry = FILE_CACHE_SIZE - 1;
file_cache.entry[file_cache.first_entry].prev_entry = file_cache.last_entry;
file_cache.entry[file_cache.last_entry].next_entry = file_cache.first_entry;
spin_unlock_irqrestore(&defex_caches_lock, flags);
}
void defex_file_cache_add(int pid, struct file *file_addr)
{
struct defex_file_cache_entry *current_entry;
struct file *old_file_addr;
unsigned long flags;
spin_lock_irqsave(&defex_caches_lock, flags);
current_entry = &file_cache.entry[file_cache.last_entry];
current_entry->pid = pid;
old_file_addr = current_entry->file_addr;
current_entry->file_addr = file_addr;
current_entry->next_entry = file_cache.first_entry;
file_cache.first_entry = file_cache.last_entry;
file_cache.last_entry = current_entry->prev_entry;
spin_unlock_irqrestore(&defex_caches_lock, flags);
if (old_file_addr) {
fput(old_file_addr);
}
}
void defex_file_cache_update(struct file *file_addr)
{
struct defex_file_cache_entry *current_entry;
struct file *old_file_addr;
unsigned long flags;
spin_lock_irqsave(&defex_caches_lock, flags);
current_entry = &file_cache.entry[file_cache.first_entry];
old_file_addr = current_entry->file_addr;
current_entry->file_addr = file_addr;
spin_unlock_irqrestore(&defex_caches_lock, flags);
if (old_file_addr)
fput(old_file_addr);
}
void defex_file_cache_delete(int pid)
{
int current_index, cache_found = 0;
struct defex_file_cache_entry *current_entry;
struct file *old_file_addr = NULL;
unsigned long flags;
spin_lock_irqsave(&defex_caches_lock, flags);
current_index = file_cache.first_entry;
do {
current_entry = &file_cache.entry[current_index];
if (current_entry->pid == pid) {
if (current_index == file_cache.first_entry) {
file_cache.first_entry = current_entry->next_entry;
file_cache.last_entry = current_index;
cache_found = 1;
break;
}
if (current_index == file_cache.last_entry) {
cache_found = 1;
break;
}
file_cache.entry[current_entry->prev_entry].next_entry = current_entry->next_entry;
file_cache.entry[current_entry->next_entry].prev_entry = current_entry->prev_entry;
file_cache.entry[file_cache.first_entry].prev_entry = current_index;
file_cache.entry[file_cache.last_entry].next_entry = current_index;
current_entry->next_entry = file_cache.first_entry;
current_entry->prev_entry = file_cache.last_entry;
file_cache.last_entry = current_index;
cache_found = 1;
break;
}
current_index = current_entry->next_entry;
} while (current_index != file_cache.first_entry);
if (cache_found) {
old_file_addr = current_entry->file_addr;
current_entry->pid = -1;
current_entry->file_addr = NULL;
}
spin_unlock_irqrestore(&defex_caches_lock, flags);
if (old_file_addr)
fput(old_file_addr);
return;
}
struct file *defex_file_cache_find(int pid)
{
int current_index, cache_found = 0;
struct defex_file_cache_entry *current_entry;
unsigned long flags;
spin_lock_irqsave(&defex_caches_lock, flags);
current_index = file_cache.first_entry;
do {
current_entry = &file_cache.entry[current_index];
if (current_entry->pid == pid) {
if (current_index == file_cache.first_entry) {
cache_found = 1;
break;
}
if (current_index == file_cache.last_entry) {
current_entry->next_entry = file_cache.first_entry;
file_cache.first_entry = file_cache.last_entry;
file_cache.last_entry = current_entry->prev_entry;
cache_found = 1;
break;
}
file_cache.entry[current_entry->prev_entry].next_entry = current_entry->next_entry;
file_cache.entry[current_entry->next_entry].prev_entry = current_entry->prev_entry;
file_cache.entry[file_cache.first_entry].prev_entry = current_index;
file_cache.entry[file_cache.last_entry].next_entry = current_index;
current_entry->next_entry = file_cache.first_entry;
current_entry->prev_entry = file_cache.last_entry;
file_cache.first_entry = current_index;
cache_found = 1;
break;
}
current_index = current_entry->next_entry;
} while (current_index != file_cache.first_entry);
spin_unlock_irqrestore(&defex_caches_lock, flags);
return (!cache_found)?NULL:current_entry->file_addr;
}

View file

@ -1,50 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/syscalls.h>
#include <linux/unistd.h>
#include "include/defex_catch_list.h"
#ifdef DEFEX_KUNIT_ENABLED
#ifndef __NR_syscalls
#define __NR_syscalls 436
#endif
#endif
#define DEFEX_CATCH_COUNT __NR_syscalls
const int defex_nr_syscalls = DEFEX_CATCH_COUNT;
#include "defex_catch_list.inc"
const struct local_syscall_struct *get_local_syscall(int syscall_no)
{
if ((unsigned int)syscall_no >= __NR_syscalls)
return NULL;
if (!syscall_catch_arr[syscall_no].local_syscall && !syscall_catch_arr[syscall_no].err_code && syscall_no) {
return &syscall_catch_arr[0];
}
return &syscall_catch_arr[syscall_no];
}
int syscall_local2global(int syscall_no)
{
int i;
for (i = 0; i < __NR_syscalls; i++) {
if (syscall_catch_arr[i].local_syscall == syscall_no)
return i;
}
return 0;
}

View file

@ -1,575 +0,0 @@
static const struct local_syscall_struct syscall_catch_arr[DEFEX_CATCH_COUNT] = {
/* */
[0] = {
.local_syscall = 0,
.err_code = -EPERM
},
#ifdef __NR_rmdir
SYSCALL_CATCH(rmdir, -EPERM),
#endif
#ifdef __NR_utimes
SYSCALL_CATCH(utimes, -EPERM),
#endif
#ifdef __NR_stat
SYSCALL_CATCH(stat, -EACCES),
#endif
#ifdef __NR_lstat
SYSCALL_CATCH(lstat, -EACCES),
#endif
#ifdef __NR_umount
SYSCALL_CATCH(umount, -EPERM),
#endif
#ifdef __NR_utime
SYSCALL_CATCH(utime, -EPERM),
#endif
#ifdef __NR_futimesat
SYSCALL_CATCH(futimesat, -EPERM),
#endif
#ifdef __NR_uselib
SYSCALL_CATCH(uselib, -EACCES),
#endif
#ifdef __NR_send
SYSCALL_CATCH(send, -EACCES),
#endif
#ifdef __NR_ustat
SYSCALL_CATCH(ustat, -EFAULT),
#endif
#ifdef __NR_getdents
SYSCALL_CATCH(getdents, -EFAULT),
#endif
#ifdef __NR_recv
SYSCALL_CATCH(recv, -EFAULT),
#endif
#ifdef __NR_fork
SYSCALL_CATCH(fork, -EINVAL),
#endif
#ifdef __NR_vfork
SYSCALL_CATCH(vfork, -EINVAL),
#endif
#ifdef __NR_sigprocmask
SYSCALL_CATCH(sigprocmask, -EFAULT),
#endif
#ifdef __NR_sigpending
SYSCALL_CATCH(sigpending, -EFAULT),
#endif
#ifdef __NR_sigaction
SYSCALL_CATCH(sigaction, -EFAULT),
#endif
#ifdef __NR_sigaltstack
SYSCALL_CATCH(sigaltstack, -EFAULT),
#endif
#ifdef __NR_sigsuspend
SYSCALL_CATCH(sigsuspend, -EFAULT),
#endif
#ifdef __NR_truncate64
SYSCALL_CATCH(truncate64, -EPERM),
#endif
#ifdef __NR_ftruncate64
SYSCALL_CATCH(ftruncate64, -EPERM),
#endif
#ifdef __NR_fstat64
SYSCALL_CATCH(fstat64, -EACCES),
#endif
#ifdef __NR_fstatat64
SYSCALL_CATCH(fstatat64, -EACCES),
#endif
#ifdef __NR_statfs64
SYSCALL_CATCH(statfs64, -EACCES),
#endif
#ifdef __NR_stat64
SYSCALL_CATCH(stat64, -EACCES),
#endif
#ifdef __NR_lstat64
SYSCALL_CATCH(lstat64, -EACCES),
#endif
#ifdef __NR_eventfd
SYSCALL_CATCH(eventfd, -EINVAL),
#endif
#ifdef __NR_epoll_create
SYSCALL_CATCH(epoll_create, -EINVAL),
#endif
#ifdef __NR_shmget
SYSCALL_CATCH(shmget, -EPERM),
#endif
#ifdef __NR_shmctl
SYSCALL_CATCH(shmctl, -EPERM),
#endif
#ifdef __NR_semctl
SYSCALL_CATCH(semctl, -EPERM),
#endif
#ifdef __NR_move_pages
SYSCALL_CATCH(move_pages, -EPERM),
#endif
#ifdef __NR_lookup_dcookie
SYSCALL_CATCH(lookup_dcookie, -EPERM),
#endif
#ifdef __NR_truncate
SYSCALL_CATCH(truncate, -EPERM),
#endif
#ifdef __NR_ftruncate
SYSCALL_CATCH(ftruncate, -EPERM),
#endif
#ifdef __NR_chdir
SYSCALL_CATCH(chdir, -EPERM),
#endif
#ifdef __NR_chroot
SYSCALL_CATCH(chroot, -EPERM),
#endif
#ifdef __NR_fchmod
SYSCALL_CATCH(fchmod, -EPERM),
#endif
#ifdef __NR_fchmodat
SYSCALL_CATCH(fchmodat, -EPERM),
#endif
#ifdef __NR_fchownat
SYSCALL_CATCH(fchownat, -EPERM),
#endif
#ifdef __NR_fchown
SYSCALL_CATCH(fchown, -EPERM),
#endif
#ifdef __NR_mknodat
SYSCALL_CATCH(mknodat, -EPERM),
#endif
#ifdef __NR_mkdirat
SYSCALL_CATCH(mkdirat, -EPERM),
#endif
#ifdef __NR_unlinkat
SYSCALL_CATCH(unlinkat, -EPERM),
#endif
#ifdef __NR_symlinkat
SYSCALL_CATCH(symlinkat, -EPERM),
#endif
#ifdef __NR_linkat
SYSCALL_CATCH(linkat, -EPERM),
#endif
#ifdef __NR_renameat
SYSCALL_CATCH(renameat, -EPERM),
#endif
#ifdef __NR_umount2
SYSCALL_CATCH(umount2, -EPERM),
#endif
#ifdef __NR_mount
SYSCALL_CATCH(mount, -EPERM),
#endif
#ifdef __NR_pivot_root
SYSCALL_CATCH(pivot_root, -EPERM),
#endif
#ifdef __NR_utimensat
SYSCALL_CATCH(utimensat, -EPERM),
#endif
#ifdef __NR_fcntl
SYSCALL_CATCH(fcntl, -EPERM),
#endif
#ifdef __NR_kexec_load
SYSCALL_CATCH(kexec_load, -EPERM),
#endif
#ifdef __NR_ptrace
SYSCALL_CATCH(ptrace, -EPERM),
#endif
#ifdef __NR_setgroups
SYSCALL_CATCH(setgroups, -EPERM),
#endif
#ifdef __NR_settimeofday
SYSCALL_CATCH(settimeofday, -EPERM),
#endif
#ifdef __NR_delete_module
SYSCALL_CATCH(delete_module, -EPERM),
#endif
#ifdef __NR_init_module
SYSCALL_CATCH(init_module, -EPERM),
#endif
#ifdef __NR_capset
SYSCALL_CATCH(capset, -EPERM),
#endif
#ifdef __NR_setpriority
SYSCALL_CATCH(setpriority, -EPERM),
#endif
#ifdef __NR_setregid
SYSCALL_CATCH(setregid, -EPERM),
#endif
#ifdef __NR_setfsuid
SYSCALL_CATCH(setfsuid, 0), //-EPERM
#endif
#ifdef __NR_setfsgid
SYSCALL_CATCH(setfsgid, 0), //-EPERM
#endif
#ifdef __NR_umask
SYSCALL_CATCH(umask, 0), //-EPERM
#endif
#ifdef __NR_setgid
SYSCALL_CATCH(setgid, -EPERM),
#endif
#ifdef __NR_setreuid
SYSCALL_CATCH(setreuid, -EPERM),
#endif
#ifdef __NR_setuid
SYSCALL_CATCH(setuid, -EPERM),
#endif
#ifdef __NR_setresuid
SYSCALL_CATCH(setresuid, -EPERM),
#endif
#ifdef __NR_setresgid
SYSCALL_CATCH(setresgid, -EPERM),
#endif
#ifdef __NR_setpgid
SYSCALL_CATCH(setpgid, -EPERM),
#endif
#ifdef __NR_getsid
SYSCALL_CATCH(getsid, -EPERM),
#endif
#ifdef __NR_setsid
SYSCALL_CATCH(setsid, -EPERM),
#endif
#ifdef __NR_sethostname
SYSCALL_CATCH(sethostname, -EPERM),
#endif
#ifdef __NR_setdomainname
SYSCALL_CATCH(setdomainname, -EPERM),
#endif
#ifdef __NR_setrlimit
SYSCALL_CATCH(setrlimit, -EPERM),
#endif
#ifdef __NR_prctl
SYSCALL_CATCH(prctl, -EPERM),
#endif
#ifdef __NR_getcpu
SYSCALL_CATCH(getcpu, -EPERM),
#endif
#ifdef __NR_kill
SYSCALL_CATCH(kill, -EPERM),
#endif
#ifdef __NR_tgkill
SYSCALL_CATCH(tgkill, -EPERM),
#endif
#ifdef __NR_tkill
SYSCALL_CATCH(tkill, -EPERM),
#endif
#ifdef __NR_rt_tgsigqueueinfo
SYSCALL_CATCH(rt_tgsigqueueinfo, -EPERM),
#endif
#ifdef __NR_rt_sigqueueinfo
SYSCALL_CATCH(rt_sigqueueinfo, -EPERM),
#endif
#ifdef __NR_listen
SYSCALL_CATCH(listen, -EPERM),
#endif
#ifdef __NR_accept
SYSCALL_CATCH(accept, -EPERM),
#endif
#ifdef __NR_open
SYSCALL_CATCH(open, 0), //-EPERM
#endif
#ifdef __NR_openat
SYSCALL_CATCH(openat, 0), //-EPERM
#endif
#ifdef __NR_shutdown
SYSCALL_CATCH(shutdown, -EBADF),
#endif
#ifdef __NR_shmat
SYSCALL_CATCH(shmat, -EACCES),
#endif
#ifdef __NR_shmdt
SYSCALL_CATCH(shmdt, -EACCES),
#endif
#ifdef __NR_semget
SYSCALL_CATCH(semget, -EACCES),
#endif
#ifdef __NR_semop
SYSCALL_CATCH(semop, -EACCES),
#endif
#ifdef __NR_faccessat
SYSCALL_CATCH(faccessat, -EACCES),
#endif
#ifdef __NR_fchdir
SYSCALL_CATCH(fchdir, -EACCES),
#endif
#ifdef __NR_fstat
SYSCALL_CATCH(fstat, -EACCES),
#endif
#ifdef __NR_readlinkat
SYSCALL_CATCH(readlinkat, -EACCES),
#endif
#ifdef __NR_statfs
SYSCALL_CATCH(statfs, -EACCES),
#endif
#ifdef __NR_fstatfs
SYSCALL_CATCH(fstatfs, -EACCES),
#endif
#ifdef __NR_getcwd
SYSCALL_CATCH(getcwd, -EACCES),
#endif
#ifdef __NR_futex
SYSCALL_CATCH(futex, -EACCES),
#endif
#ifdef __NR_perf_event_open
SYSCALL_CATCH(perf_event_open, -EACCES),
#endif
#ifdef __NR_socket
SYSCALL_CATCH(socket, -EACCES),
#endif
#ifdef __NR_bind
SYSCALL_CATCH(bind, -EACCES),
#endif
#ifdef __NR_connect
SYSCALL_CATCH(connect, -EACCES),
#endif
#ifdef __NR_sendto
SYSCALL_CATCH(sendto, -EACCES),
#endif
#ifdef __NR_mprotect
SYSCALL_CATCH(mprotect, -EACCES),
#endif
#ifdef __NR_mremap
SYSCALL_CATCH(mremap, -EFAULT),
#endif
#ifdef __NR_pselect6
SYSCALL_CATCH(pselect6, -EFAULT),
#endif
#ifdef __NR_ioctl
SYSCALL_CATCH(ioctl, -EFAULT),
#endif
#ifdef __NR_ioprio_set
SYSCALL_CATCH(ioprio_set, -EFAULT),
#endif
#ifdef __NR_pipe2
SYSCALL_CATCH(pipe2, -EFAULT),
#endif
#ifdef __NR_getdents64
SYSCALL_CATCH(getdents64, -EFAULT),
#endif
#ifdef __NR_setitimer
SYSCALL_CATCH(setitimer, -EFAULT),
#endif
#ifdef __NR_capget
SYSCALL_CATCH(capget, -EFAULT),
#endif
#ifdef __NR_getresuid
SYSCALL_CATCH(getresuid, -EFAULT),
#endif
#ifdef __NR_getresgid
SYSCALL_CATCH(getresgid, -EFAULT),
#endif
#ifdef __NR_rt_sigprocmask
SYSCALL_CATCH(rt_sigprocmask, 0), //-EFAULT, Skip this syscall due to HTML5 score of Geekbench5.4
#endif
#ifdef __NR_socketpair
SYSCALL_CATCH(socketpair, -EFAULT),
#endif
#ifdef __NR_getsockname
SYSCALL_CATCH(getsockname, -EFAULT),
#endif
#ifdef __NR_getpeername
SYSCALL_CATCH(getpeername, -EFAULT),
#endif
#ifdef __NR_recvfrom
SYSCALL_CATCH(recvfrom, -EFAULT),
#endif
#ifdef __NR_setsockopt
SYSCALL_CATCH(setsockopt, -EFAULT),
#endif
#ifdef __NR_sendmsg
SYSCALL_CATCH(sendmsg, -EFAULT),
#endif
#ifdef __NR_recvmsg
SYSCALL_CATCH(recvmsg, -EFAULT),
#endif
#ifdef __NR_rt_sigsuspend
SYSCALL_CATCH(rt_sigsuspend, -EFAULT),
#endif
#ifdef __NR_rt_sigpending
SYSCALL_CATCH(rt_sigpending, -EFAULT),
#endif
#ifdef __NR_rt_sigaction
SYSCALL_CATCH(rt_sigaction, -EFAULT),
#endif
#ifdef __NR_remap_file_pages
SYSCALL_CATCH(remap_file_pages, -EINVAL),
#endif
#ifdef __NR_ppoll
SYSCALL_CATCH(ppoll, -EINVAL),
#endif
#ifdef __NR_dup
SYSCALL_CATCH(dup, -EINVAL),
#endif
#ifdef __NR_dup3
SYSCALL_CATCH(dup3, -EINVAL),
#endif
#ifdef __NR_eventfd2
SYSCALL_CATCH(eventfd2, -EINVAL),
#endif
#ifdef __NR_timerfd_create
SYSCALL_CATCH(timerfd_create, -EINVAL),
#endif
#ifdef __NR_timerfd_gettime
SYSCALL_CATCH(timerfd_gettime, -EINVAL),
#endif
#ifdef __NR_timerfd_settime
SYSCALL_CATCH(timerfd_settime, -EINVAL),
#endif
#ifdef __NR_epoll_create1
SYSCALL_CATCH(epoll_create1, -EINVAL),
#endif
#ifdef __NR_rt_sigtimedwait
SYSCALL_CATCH(rt_sigtimedwait, -EINVAL),
#endif
#ifdef __NR_setxattr
SYSCALL_CATCH(setxattr, -EEXIST),
#endif
#ifdef __NR_lsetxattr
SYSCALL_CATCH(lsetxattr, -EEXIST),
#endif
#ifdef __NR_fsetxattr
SYSCALL_CATCH(fsetxattr, -EEXIST),
#endif
#ifdef __NR_removexattr
SYSCALL_CATCH(removexattr, -EEXIST),
#endif
#ifdef __NR_lremovexattr
SYSCALL_CATCH(lremovexattr, -EEXIST),
#endif
#ifdef __NR_fremovexattr
SYSCALL_CATCH(fremovexattr, -EEXIST),
#endif
#ifdef __NR_inotify_init1
SYSCALL_CATCH(inotify_init1, -EMFILE),
#endif
#ifdef __NR_clone
SYSCALL_CATCH(clone, -EINVAL),
#endif
#ifdef __NR_execve
SYSCALL_CATCH(execve, 0), //-EPERM
#endif
#ifdef __NR_write
SYSCALL_CATCH(write, 0), //-EPERM
#endif
#ifdef __NR_writev
SYSCALL_CATCH(writev, -EPERM),
#endif
#ifdef __NR_pwrite64
SYSCALL_CATCH(pwrite64, -EPERM),
#endif
#ifdef __NR_pwritev
SYSCALL_CATCH(pwritev, -EPERM),
#endif
#ifdef __NR_epoll_ctl
SYSCALL_CATCH(epoll_ctl, -EINVAL),
#endif
#ifdef __NR_epoll_pwait
SYSCALL_CATCH(epoll_pwait, -EINVAL),
#endif
#ifdef __NR_sendfile
SYSCALL_CATCH(sendfile, -EFAULT),
#endif
#ifdef __NR_signalfd4
SYSCALL_CATCH(signalfd4, -EFAULT),
#endif
#ifdef __NR_vmsplice
SYSCALL_CATCH(vmsplice, -EINVAL),
#endif
#ifdef __NR_splice
SYSCALL_CATCH(splice, -EINVAL),
#endif
#ifdef __NR_tee
SYSCALL_CATCH(tee, -EINVAL),
#endif
#ifdef __NR_fsync
SYSCALL_CATCH(fsync, -EINVAL),
#endif
#ifdef __NR_fdatasync
SYSCALL_CATCH(fdatasync, -EINVAL),
#endif
#ifdef __NR_sync_file_range
SYSCALL_CATCH(sync_file_range, -EINVAL),
#endif
#ifdef __NR_acct
SYSCALL_CATCH(acct, -EPERM),
#endif
#ifdef __NR_sched_setparam
SYSCALL_CATCH(sched_setparam, -EINVAL),
#endif
#ifdef __NR_sched_setscheduler
SYSCALL_CATCH(sched_setscheduler, -EINVAL),
#endif
#ifdef __NR_sched_setaffinity
SYSCALL_CATCH(sched_setaffinity, -EINVAL),
#endif
#ifdef __NR_reboot
SYSCALL_CATCH(reboot, -EPERM),
#endif
#ifdef __NR_mq_timedsend
SYSCALL_CATCH(mq_timedsend, -EINVAL),
#endif
#ifdef __NR_mq_timedreceive
SYSCALL_CATCH(mq_timedreceive, -EBADF),
#endif
#ifdef __NR_msgrcv
SYSCALL_CATCH(msgrcv, -EINVAL),
#endif
#ifdef __NR_msgsnd
SYSCALL_CATCH(msgsnd, -EFAULT),
#endif
#ifdef __NR_semtimedop
SYSCALL_CATCH(semtimedop, -EINVAL),
#endif
#ifdef __NR_add_key
SYSCALL_CATCH(add_key, -EINVAL),
#endif
#ifdef __NR_request_key
SYSCALL_CATCH(request_key, -EINVAL),
#endif
#ifdef __NR_keyctl
SYSCALL_CATCH(keyctl, -EOPNOTSUPP),
#endif
#ifdef __NR_mmap
SYSCALL_CATCH(mmap, -EINVAL),
#endif
#ifdef __NR_mincore
SYSCALL_CATCH(mincore, -EINVAL),
#endif
#ifdef __NR_mbind
SYSCALL_CATCH(mbind, -EINVAL),
#endif
#ifdef __NR_set_mempolicy
SYSCALL_CATCH(set_mempolicy, -EINVAL),
#endif
#ifdef __NR_migrate_pages
SYSCALL_CATCH(migrate_pages, -EINVAL),
#endif
#ifdef __NR_accept4
SYSCALL_CATCH(accept4, -EINVAL),
#endif
#ifdef __NR_recvmmsg
SYSCALL_CATCH(recvmmsg, -EINVAL),
#endif
#ifdef __NR_link
SYSCALL_CATCH(link, -EPERM),
#endif
#ifdef __NR_unlink
SYSCALL_CATCH(unlink, -EPERM),
#endif
#ifdef __NR_mknod
SYSCALL_CATCH(mknod, -EPERM),
#endif
#ifdef __NR_chmod
SYSCALL_CATCH(chmod, -EPERM),
#endif
#ifdef __NR_chown
SYSCALL_CATCH(chown, -EPERM),
#endif
#ifdef __NR_mkdir
SYSCALL_CATCH(mkdir, -EPERM),
#endif
#ifdef __NR_lchown
SYSCALL_CATCH(lchown, -EPERM),
#endif
#ifdef __NR_rename
SYSCALL_CATCH(rename, -EPERM),
#endif
#ifdef __NR_epoll_wait
SYSCALL_CATCH(epoll_wait, -EINVAL),
#endif
#ifdef __NR_sysctl
SYSCALL_CATCH(sysctl, -EFAULT),
#endif
};

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <asm/unistd32.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/version.h>
#include "include/defex_catch_list.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
#define __COMPAT_SYSCALL_NR
#include <asm/unistd.h>
#else
#ifdef __NR_clone3
#define __NR_compat_syscalls (__NR_clone3 + 10)
#elif defined(__NR_rseq)
#define __NR_compat_syscalls (__NR_rseq + 10)
#elif defined(__NR_seccomp)
#define __NR_compat_syscalls (__NR_seccomp + 10)
#else
#define __NR_compat_syscalls 400
#endif
#endif /* < KERNEL_VERSION(4, 0, 0) */
#define DEFEX_CATCH_COUNT __NR_compat_syscalls
const int defex_nr_syscalls_compat = DEFEX_CATCH_COUNT;
#include "defex_catch_list.inc"
const struct local_syscall_struct *get_local_syscall_compat(int syscall_no)
{
if ((unsigned int)syscall_no >= __NR_compat_syscalls)
return NULL;
if (!syscall_catch_arr[syscall_no].local_syscall && !syscall_catch_arr[syscall_no].err_code && syscall_no) {
return &syscall_catch_arr[0];
}
return &syscall_catch_arr[syscall_no];
}

View file

@ -1,389 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/version.h>
#include "include/defex_catch_list.h"
#include "include/defex_internal.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/mm.h>
#include <linux/sched/task.h>
#endif
#define MAX_PID_32 32768
#define DEFEX_MEM_CACHE_SIZE 32
#define DEFEX_MEM_CACHE_COUNT 3
#define CACHE_CRED_DATA 0
#define CACHE_CRED_DATA_ID 1
#define CACHE_HTABLE_ITEM 2
struct id_set {
unsigned int uid, fsuid, egid;
};
struct proc_cred_data {
unsigned short cred_flags;
unsigned short tcnt;
struct id_set default_ids;
struct id_set main_ids[];
};
struct hash_item_struct {
struct hlist_node node;
struct proc_cred_data *cred_data;
int id;
};
struct mem_cache_list {
atomic_t count;
char name[8];
struct kmem_cache *allocator;
void *mem_cache_array[DEFEX_MEM_CACHE_SIZE];
};
#ifdef DEFEX_PED_ENABLE
DECLARE_HASHTABLE(creds_hash, 15);
__visible_for_testing DEFINE_SPINLOCK(creds_hash_update_lock);
static struct proc_cred_data *creds_fast_hash[MAX_PID_32 + 1];
__visible_for_testing struct mem_cache_list mem_cache[DEFEX_MEM_CACHE_COUNT];
static int creds_fast_hash_ready __ro_after_init;
__visible_for_testing void mem_cache_alloc(void);
void __init creds_fast_hash_init(void)
{
unsigned int i;
static const int sizes[DEFEX_MEM_CACHE_COUNT] __initdata = {
sizeof(struct proc_cred_data),
sizeof(struct proc_cred_data) + sizeof(struct id_set),
sizeof(struct hash_item_struct)
};
hash_init(creds_hash);
for (i = 0; i <= MAX_PID_32; i++)
creds_fast_hash[i] = NULL;
for(i = 0; i < ARRAY_SIZE(sizes); i++) {
snprintf(mem_cache[i].name, sizeof(mem_cache[i].name), "defex%d", i);
mem_cache[i].allocator = kmem_cache_create(mem_cache[i].name, sizes[i], 0, 0, NULL);
}
for(i = 0; i < (DEFEX_MEM_CACHE_SIZE / 2); i++)
mem_cache_alloc();
creds_fast_hash_ready = 1;
}
int is_task_creds_ready(void)
{
return creds_fast_hash_ready;
}
__visible_for_testing inline void *mem_cache_get(int cache_number)
{
int n;
n = atomic_read(&mem_cache[cache_number].count);
if (n) {
atomic_dec(&mem_cache[cache_number].count);
return mem_cache[cache_number].mem_cache_array[n - 1];
}
return NULL;
}
__visible_for_testing inline void *mem_cache_reclaim(int cache_number, void *ptr)
{
int n;
n = atomic_read(&mem_cache[cache_number].count);
if (n < DEFEX_MEM_CACHE_SIZE) {
atomic_inc(&mem_cache[cache_number].count);
mem_cache[cache_number].mem_cache_array[n] = ptr;
ptr = NULL;
}
return ptr;
}
__visible_for_testing void mem_cache_alloc(void)
{
int mem_allocated = 0;
int i, n;
unsigned long flags;
void *mem_block[DEFEX_MEM_CACHE_COUNT];
for(i = 0; i < DEFEX_MEM_CACHE_COUNT; i++) {
mem_block[i] = NULL;
n = atomic_read(&mem_cache[i].count);
if (n < (DEFEX_MEM_CACHE_SIZE / 2)) {
mem_block[i] = kmem_cache_alloc(mem_cache[i].allocator, in_atomic() ? GFP_ATOMIC:GFP_KERNEL);
mem_allocated++;
}
}
if (!mem_allocated)
return;
spin_lock_irqsave(&creds_hash_update_lock, flags);
for(i = 0; i < DEFEX_MEM_CACHE_COUNT; i++) {
n = atomic_read(&mem_cache[i].count);
if (mem_block[i] && n < DEFEX_MEM_CACHE_SIZE) {
mem_cache[i].mem_cache_array[n] = mem_block[i];
mem_block[i] = NULL;
atomic_inc(&mem_cache[i].count);
mem_allocated--;
}
}
spin_unlock_irqrestore(&creds_hash_update_lock, flags);
if (!mem_allocated)
return;
for(i = 0; i < DEFEX_MEM_CACHE_COUNT; i++) {
if (mem_block[i]) {
kmem_cache_free(mem_cache[i].allocator, mem_block[i]);
}
}
}
__visible_for_testing struct proc_cred_data *get_cred_data(int id)
{
struct proc_cred_data *cred_data = NULL;
struct hash_item_struct *obj;
if (id < 0)
return NULL;
if (id <= MAX_PID_32) {
cred_data = creds_fast_hash[id];
} else {
hash_for_each_possible(creds_hash, obj, node, id) {
if (obj->id == id) {
cred_data = obj->cred_data;
break;
}
}
}
return cred_data;
}
__visible_for_testing struct proc_cred_data **get_cred_ptr(int id)
{
struct proc_cred_data **cred_ptr = NULL;
struct hash_item_struct *obj;
if (id < 0)
return NULL;
if (id <= MAX_PID_32) {
cred_ptr = &creds_fast_hash[id];
} else {
hash_for_each_possible(creds_hash, obj, node, id) {
if (obj->id == id) {
cred_ptr = &obj->cred_data;
break;
}
}
}
return cred_ptr;
}
__visible_for_testing void set_cred_data(int id, struct proc_cred_data **cred_ptr, struct proc_cred_data *cred_data)
{
struct hash_item_struct *obj;
if (id < 0)
return;
if (cred_ptr) {
*cred_ptr = cred_data;
} else {
if (id > MAX_PID_32) {
obj = mem_cache_get(CACHE_HTABLE_ITEM);
if (!obj)
return;
obj->id = id;
obj->cred_data = cred_data;
hash_add(creds_hash, &obj->node, id);
}
}
}
void get_task_creds(struct task_struct *p, unsigned int *uid_ptr, unsigned int *fsuid_ptr, unsigned int *egid_ptr, unsigned short *cred_flags_ptr)
{
struct proc_cred_data *cred_data, *thread_cred_data;
struct id_set *ids_ptr;
unsigned int uid = 0, fsuid = 0, egid = 0;
unsigned short cred_flags = CRED_FLAGS_PROOT;
unsigned long flags;
int tgid = p->tgid, pid = p->pid;
spin_lock_irqsave(&creds_hash_update_lock, flags);
cred_data = get_cred_data(tgid);
if (cred_data) {
if (tgid == pid) {
ids_ptr = (cred_data->cred_flags & CRED_FLAGS_MAIN_UPDATED) ? \
(&cred_data->main_ids[0]) : (&cred_data->default_ids);
} else {
if (cred_data->cred_flags & CRED_FLAGS_SUB_UPDATED) {
thread_cred_data = get_cred_data(pid);
if (thread_cred_data)
cred_data = thread_cred_data;
}
ids_ptr = &cred_data->default_ids;
}
GET_CREDS(ids_ptr, cred_data);
}
spin_unlock_irqrestore(&creds_hash_update_lock, flags);
*uid_ptr = uid;
*fsuid_ptr = fsuid;
*egid_ptr = egid;
*cred_flags_ptr = cred_flags;
}
int set_task_creds(struct task_struct *p, unsigned int uid, unsigned int fsuid, unsigned int egid, unsigned short cred_flags)
{
struct proc_cred_data *cred_data = NULL, *tmp_data, **cred_ptr;
struct id_set *ids_ptr;
unsigned long flags;
int err = -1, tgid = p->tgid, pid = p->pid;
void *free_buff = NULL;
mem_cache_alloc();
spin_lock_irqsave(&creds_hash_update_lock, flags);
/* Search for main proces's data */
cred_ptr = get_cred_ptr(tgid);
cred_data = (cred_ptr) ? (*cred_ptr) : NULL;
if (!cred_data) {
/* Not found? Allocate a new data */
cred_data = mem_cache_get(CACHE_CRED_DATA);
if (!cred_data)
goto set_finish;
cred_data->cred_flags = 0;
cred_data->tcnt = 1;
set_cred_data(tgid, cred_ptr, cred_data);
}
ids_ptr = &cred_data->default_ids;
if (cred_data->tcnt >= 2) {
if (tgid == pid) {
/* Allocate extended data for main process, copy and remove old data */
if (!(cred_data->cred_flags & CRED_FLAGS_MAIN_UPDATED)) {
cred_data->cred_flags |= CRED_FLAGS_MAIN_UPDATED;
tmp_data = mem_cache_get(CACHE_CRED_DATA_ID);
if (!tmp_data)
goto set_finish;
*tmp_data = *cred_data;
free_buff = mem_cache_reclaim(CACHE_CRED_DATA, cred_data);
cred_data = tmp_data;
set_cred_data(tgid, cred_ptr, cred_data);
}
ids_ptr = &cred_data->main_ids[0];
} else {
cred_data->cred_flags |= CRED_FLAGS_SUB_UPDATED;
/* Search for thread's data. Allocate, if not found */
cred_ptr = get_cred_ptr(pid);
cred_data = (cred_ptr) ? (*cred_ptr) : NULL;
if (!cred_data) {
cred_data = mem_cache_get(CACHE_CRED_DATA);
if (!cred_data)
goto set_finish;
set_cred_data(pid, cred_ptr, cred_data);
}
cred_data->cred_flags = 0;
ids_ptr = &cred_data->default_ids;
}
}
SET_CREDS(ids_ptr, cred_data);
err = 0;
set_finish:
spin_unlock_irqrestore(&creds_hash_update_lock, flags);
/* Free the pending pointer */
if (free_buff)
kmem_cache_free(mem_cache[CACHE_CRED_DATA].allocator, free_buff);
mem_cache_alloc();
return err;
}
void set_task_creds_tcnt(struct task_struct *p, int addition)
{
struct hash_item_struct *tgid_obj = NULL, *pid_obj = NULL;
struct proc_cred_data **cred_ptr, *tgid_cred_data = NULL, *pid_cred_data = NULL;
struct proc_cred_data *free_buff1 = NULL, *free_buff2 = NULL;
int tgid = p->tgid, pid = p->pid;
unsigned long flags;
spin_lock_irqsave(&creds_hash_update_lock, flags);
/* Remove the thread's data, if found */
if (tgid != pid && addition == -1) {
cred_ptr = get_cred_ptr(pid);
pid_cred_data = (cred_ptr) ? (*cred_ptr) : NULL;
if (pid_cred_data) {
*cred_ptr = NULL;
/* Return to pre-allocated pool, if possible */
free_buff1 = mem_cache_reclaim(CACHE_CRED_DATA, pid_cred_data);
}
/* Remove the thread's hash container */
if (cred_ptr && pid > MAX_PID_32) {
pid_obj = container_of(cred_ptr, struct hash_item_struct, cred_data);
hash_del(&pid_obj->node);
/* Return to pre-allocated pool, if possible */
pid_obj = mem_cache_reclaim(CACHE_HTABLE_ITEM, pid_obj);
}
}
/* Search for the main process's data */
cred_ptr = get_cred_ptr(tgid);
tgid_cred_data = (cred_ptr) ? (*cred_ptr) : NULL;
if (tgid_cred_data) {
tgid_cred_data->tcnt += addition;
/* No threads, remove process data */
if (!tgid_cred_data->tcnt) {
*cred_ptr = NULL;
/* Return to pre-allocated pool, if possible */
free_buff2 = mem_cache_reclaim((tgid_cred_data->cred_flags & CRED_FLAGS_MAIN_UPDATED) ? \
CACHE_CRED_DATA_ID : CACHE_CRED_DATA, tgid_cred_data);
/* Remove the process's hash container */
if (tgid > MAX_PID_32) {
tgid_obj = container_of(cred_ptr, struct hash_item_struct, cred_data);
hash_del(&tgid_obj->node);
/* Return to pre-allocated pool, if possible */
tgid_obj = mem_cache_reclaim(CACHE_HTABLE_ITEM, tgid_obj);
}
}
}
spin_unlock_irqrestore(&creds_hash_update_lock, flags);
/* Free all pending pointers */
if (free_buff1)
kmem_cache_free(mem_cache[CACHE_CRED_DATA].allocator, free_buff1);
if (free_buff2)
kmem_cache_free(mem_cache[(free_buff2->cred_flags & CRED_FLAGS_MAIN_UPDATED) ? \
CACHE_CRED_DATA_ID : CACHE_CRED_DATA].allocator, free_buff2);
if (pid_obj)
kmem_cache_free(mem_cache[CACHE_HTABLE_ITEM].allocator, pid_obj);
if (tgid_obj)
kmem_cache_free(mem_cache[CACHE_HTABLE_ITEM].allocator, tgid_obj);
return;
}
#else
int is_task_creds_ready(void)
{
return 0;
}
void set_task_creds_tcnt(struct task_struct *p, int addition)
{
(void)p;
(void)addition;
}
#endif /* DEFEX_PED_ENABLE */

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/export.h>
#include <linux/init.h>
#include <linux/version.h>
__INITRODATA
.align 8
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) || !defined(VMLINUX_SYMBOL))
#define VMLINUX_SYMBOL(name) name
#endif
#define GLOBAL(name) \
.globl VMLINUX_SYMBOL(name); \
VMLINUX_SYMBOL(name):
.section ".ref.data", "aw"
GLOBAL(defex_public_key_start)
#ifdef DEFEX_DEBUG_ENABLE
.incbin "security/samsung/defex_lsm/cert/pubkey_eng.der"
#else
.incbin "security/samsung/defex_lsm/cert/pubkey_user.der"
#endif
GLOBAL(defex_public_key_end)

View file

@ -1,242 +0,0 @@
/*
* Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/cred.h>
#include <linux/crypto.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/key.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/version.h>
#include <crypto/hash.h>
#include <keys/asymmetric-type.h>
#include "include/defex_debug.h"
#include "include/defex_sign.h"
#ifdef DEFEX_KUNIT_ENABLED
#include <kunit/mock.h>
#endif
#define SHA256_DIGEST_SIZE 32
extern char defex_public_key_start[];
extern char defex_public_key_end[];
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
__visible_for_testing int defex_public_key_verify_signature(unsigned char *pub_key,
int pub_key_size,
unsigned char *signature,
unsigned char *hash_sha256)
{
(void)pub_key;
(void)pub_key_size;
(void)signature;
(void)hash_sha256;
/* Skip signarue check at kernel version < 3.7.0 */
defex_log_warn("Skip signature check in current kernel version");
return 0;
}
#else
#include <crypto/public_key.h>
static struct key *defex_keyring;
__visible_for_testing struct key *defex_keyring_alloc(const char *description,
kuid_t uid, kgid_t gid,
const struct cred *cred,
unsigned long flags)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
return keyring_alloc(description, uid, gid, cred, flags, NULL)
#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
key_perm_t perm = ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH);
return keyring_alloc(description, uid, gid, cred, perm, flags, NULL);
#else
key_perm_t perm = ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH);
return keyring_alloc(description, uid, gid, cred, perm, flags, NULL, NULL);
#endif
}
__visible_for_testing int defex_keyring_init(void)
{
int err = 0;
const struct cred *cred = current_cred();
static const char keyring_name[] = "defex_keyring";
if (defex_keyring)
return err;
defex_keyring = defex_keyring_alloc(keyring_name, KUIDT_INIT(0), KGIDT_INIT(0),
cred, KEY_ALLOC_NOT_IN_QUOTA);
if (!defex_keyring) {
err = -1;
defex_log_info("Can't allocate %s keyring (NULL)", keyring_name);
} else if (IS_ERR(defex_keyring)) {
err = PTR_ERR(defex_keyring);
defex_log_info("Can't allocate %s keyring, err=%d", keyring_name, err);
defex_keyring = NULL;
}
return err;
}
__visible_for_testing int defex_public_key_verify_signature(unsigned char *pub_key,
int pub_key_size,
unsigned char *signature,
unsigned char *hash_sha256)
{
int ret = -1;
key_ref_t key_ref;
struct key *key;
struct public_key_signature pks;
static const char key_name[] = "defex_key";
if (defex_keyring_init() != 0)
return ret;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0)
key_ref = keyring_search(make_key_ref(defex_keyring, 1), &key_type_asymmetric, key_name);
#else
key_ref = keyring_search(make_key_ref(defex_keyring, 1), &key_type_asymmetric, key_name, true);
#endif
if (IS_ERR(key_ref)) {
key_ref = key_create_or_update(make_key_ref(defex_keyring, 1),
"asymmetric",
key_name,
pub_key,
pub_key_size,
((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ),
KEY_ALLOC_NOT_IN_QUOTA);
}
if (IS_ERR(key_ref)) {
defex_log_err("Invalid key reference (%ld)", PTR_ERR(key_ref));
return ret;
}
key = key_ref_to_ptr(key_ref);
memset(&pks, 0, sizeof(pks));
pks.digest = hash_sha256;
pks.digest_size = SHA256_DIGEST_SIZE;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
pks.pkey_algo = PKEY_ALGO_RSA;
#endif
pks.pkey_hash_algo = HASH_ALGO_SHA256;
pks.nr_mpi = 1;
pks.rsa.s = mpi_read_raw_data(signature, SIGN_SIZE);
if (pks.rsa.s)
ret = verify_signature(key, &pks);
mpi_free(pks.rsa.s);
#else
pks.pkey_algo = "rsa";
pks.hash_algo = "sha256";
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
pks.encoding = "pkcs1";
#endif
pks.s = signature;
pks.s_size = SIGN_SIZE;
ret = verify_signature(key, &pks);
#endif
key_ref_put(key_ref);
return ret;
}
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) */
int defex_calc_hash(const char *data, unsigned int size, unsigned char *hash)
{
struct crypto_shash *handle;
struct shash_desc* shash;
int err = -1;
handle = crypto_alloc_shash("sha256", 0, 0);
if (IS_ERR_OR_NULL(handle)) {
defex_log_err("Can't alloc sha256");
return err;
}
shash = kzalloc(sizeof(struct shash_desc) + crypto_shash_descsize(handle), GFP_KERNEL);
if (!shash)
goto clean_handle;
shash->tfm = handle;
do {
err = crypto_shash_init(shash);
if (err < 0)
break;
err = crypto_shash_update(shash, data, size);
if (err < 0)
break;
err = crypto_shash_final(shash, hash);
if (err < 0)
break;
} while(0);
kfree(shash);
clean_handle:
crypto_free_shash(handle);
return err;
}
int defex_rules_signature_check(const char *rules_buffer, unsigned int rules_data_size, unsigned int *rules_size)
{
int res = -1;
unsigned int defex_public_key_size = (unsigned int)((defex_public_key_end - defex_public_key_start) & 0xffffffff);
unsigned char *hash_sha256;
unsigned char *hash_sha256_first;
unsigned char *signature;
unsigned char *pub_key;
if (rules_data_size < SIGN_SIZE)
return res;
hash_sha256_first = kmalloc(SHA256_DIGEST_SIZE, GFP_KERNEL);
if (!hash_sha256_first)
return res;
hash_sha256 = kmalloc(SHA256_DIGEST_SIZE, GFP_KERNEL);
if (!hash_sha256)
goto clean_hash_sha256_first;
signature = kmalloc(SIGN_SIZE, GFP_KERNEL);
if (!signature)
goto clean_hash_sha256;
pub_key = kmalloc(defex_public_key_size, GFP_KERNEL);
if (!pub_key)
goto clean_signature;
memcpy(pub_key, defex_public_key_start, defex_public_key_size);
memcpy(signature, (u8*)(rules_buffer + rules_data_size - SIGN_SIZE), SIGN_SIZE);
defex_calc_hash(rules_buffer, rules_data_size - SIGN_SIZE, hash_sha256_first);
defex_calc_hash(hash_sha256_first, SHA256_DIGEST_SIZE, hash_sha256);
#ifdef DEFEX_DEBUG_ENABLE
defex_log_info("Rules signature size = %d", SIGN_SIZE);
blob("Rules signature dump:", signature, SIGN_SIZE, 16);
defex_log_info("Key size = %d", defex_public_key_size);
blob("Key dump:", pub_key, defex_public_key_size, 16);
blob("Final hash dump:", hash_sha256, SHA256_DIGEST_SIZE, 16);
#endif
res = defex_public_key_verify_signature(pub_key, defex_public_key_size, signature, hash_sha256);
if (rules_size && !res)
*rules_size = rules_data_size - SIGN_SIZE;
kfree(pub_key);
clean_signature:
kfree(signature);
clean_hash_sha256:
kfree(hash_sha256);
clean_hash_sha256_first:
kfree(hash_sha256_first);
return res;
}

View file

@ -1,367 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/uaccess.h>
#include <linux/cred.h>
#include <linux/dcache.h>
#include <linux/err.h>
#include <linux/file.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/namei.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/unistd.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include "include/defex_caches.h"
#include "include/defex_catch_list.h"
#include "include/defex_config.h"
#include "include/defex_internal.h"
#include "include/defex_rules.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/mm.h>
#include <linux/sched/task.h>
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
#include "../security/integrity/integrity.h"
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
inline ssize_t __vfs_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
ssize_t ret;
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos);
else if (file->f_op->aio_read)
ret = do_sync_read(file, buf, count, pos);
else if (file->f_op->read_iter)
ret = new_sync_read(file, buf, count, pos);
else
ret = -EINVAL;
return ret;
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
#define GET_KERNEL_DS KERNEL_DS
#else
#define GET_KERNEL_DS get_ds()
#endif
struct file *local_fopen(const char *fname, int flags, umode_t mode)
{
struct file *f;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)
mm_segment_t old_fs;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)
old_fs = get_fs();
set_fs(GET_KERNEL_DS);
#endif
f = filp_open(fname, flags, mode);
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)
set_fs(old_fs);
#endif
return f;
}
int local_fread(struct file *f, loff_t offset, void *ptr, unsigned long bytes)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)
mm_segment_t old_fs;
char __user *buf = (char __user *)ptr;
#endif
ssize_t ret;
if (!(f->f_mode & FMODE_READ))
return -EBADF;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)
old_fs = get_fs();
set_fs(GET_KERNEL_DS);
ret = vfs_read(f, buf, bytes, &offset);
set_fs(old_fs);
#else
ret = (ssize_t)integrity_kernel_read(f, offset, ptr, bytes);
#endif
return (int)ret;
}
const char unknown_file[] = "<unknown filename>";
__visible_for_testing bool check_slab_ptr(void *ptr)
{
struct page *page;
if (IS_ERR_OR_NULL(ptr) || ptr < (void *)PAGE_SIZE || !virt_addr_valid(ptr))
return false;
page = virt_to_head_page(ptr);
return PageSlab(page);
}
int init_defex_context(struct defex_context *dc, int syscall, struct task_struct *p, struct file *f)
{
memset(dc, 0, offsetof(struct defex_context, cred));
if (check_slab_ptr(f)) {
get_file(f);
dc->target_file = f;
}
dc->syscall_no = syscall;
dc->task = p;
if (p == current)
dc->cred = (struct cred *)current_cred();
if (!dc->cred)
return 0;
return 1;
}
void release_defex_context(struct defex_context *dc)
{
kfree(dc->process_name_buff);
kfree(dc->target_name_buff);
if (dc->target_file)
fput(dc->target_file);
#ifndef DEFEX_CACHES_ENABLE
if (dc->process_file)
fput(dc->process_file);
#endif /* DEFEX_CACHES_ENABLE */
}
struct file *get_dc_process_file(struct defex_context *dc)
{
if (!dc->process_file)
dc->process_file = defex_get_source_file(dc->task);
return dc->process_file;
}
const struct path *get_dc_process_dpath(struct defex_context *dc)
{
const struct path *dpath;
struct file *exe_file = NULL;
if (dc->process_dpath)
return dc->process_dpath;
exe_file = get_dc_process_file(dc);
if (!IS_ERR_OR_NULL(exe_file)) {
dpath = &exe_file->f_path;
if (dpath->dentry && dpath->dentry->d_inode) {
dc->process_dpath = dpath;
return dpath;
}
}
return NULL;
}
char *get_dc_process_name(struct defex_context *dc)
{
const struct path *dpath;
char *path = NULL;
if (!dc->process_name) {
dpath = get_dc_process_dpath(dc);
if (dpath) {
dc->process_name_buff = kmalloc(PATH_MAX, GFP_KERNEL);
if (dc->process_name_buff)
path = d_path(dpath, dc->process_name_buff, PATH_MAX);
}
dc->process_name = (IS_ERR_OR_NULL(path)) ? (char *)unknown_file : path;
}
return dc->process_name;
}
const struct path *get_dc_target_dpath(struct defex_context *dc)
{
const struct path *dpath;
if (dc->target_dpath)
return dc->target_dpath;
if (dc->target_file) {
dpath = &(dc->target_file->f_path);
if (dpath->dentry && dpath->dentry->d_inode) {
dc->target_dpath = dpath;
return dpath;
}
}
return NULL;
}
char *get_dc_target_name(struct defex_context *dc)
{
const struct path *dpath;
char *path = NULL;
if (!dc->target_name) {
dpath = get_dc_target_dpath(dc);
if (dpath) {
dc->target_name_buff = kmalloc(PATH_MAX, GFP_KERNEL);
if (dc->target_name_buff)
path = d_path(dpath, dc->target_name_buff, PATH_MAX);
}
dc->target_name = (IS_ERR_OR_NULL(path)) ? (char *)unknown_file : path;
}
return dc->target_name;
}
struct file *defex_get_source_file(struct task_struct *p)
{
struct file *file_addr = NULL;
struct mm_struct *proc_mm;
#ifdef DEFEX_CACHES_ENABLE
bool self;
file_addr = defex_file_cache_find(p->pid);
if (!file_addr) {
proc_mm = get_task_mm(p);
if (!proc_mm)
return NULL;
file_addr = get_mm_exe_file(proc_mm);
mmput(proc_mm);
if (!file_addr)
return NULL;
defex_file_cache_add(p->pid, file_addr);
} else {
self = (p == current);
proc_mm = (self)?p->mm:get_task_mm(p);
if (!proc_mm)
return NULL;
if (self)
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0)
if (!down_read_trylock(&proc_mm->mmap_sem))
return NULL;
#else
if (!down_read_trylock(&proc_mm->mmap_lock))
return NULL;
#endif
if (file_addr != proc_mm->exe_file) {
file_addr = proc_mm->exe_file;
if (!file_addr)
goto clean_mm;
get_file(file_addr);
defex_file_cache_update(file_addr);
}
clean_mm:
if (self)
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0)
up_read(&proc_mm->mmap_sem);
#else
up_read(&proc_mm->mmap_lock);
#endif
else
mmput(proc_mm);
}
#else
proc_mm = get_task_mm(p);
if (!proc_mm)
return NULL;
file_addr = get_mm_exe_file(proc_mm);
mmput(proc_mm);
#endif /* DEFEX_CACHES_ENABLE */
return file_addr;
}
char *defex_get_filename(struct task_struct *p)
{
struct file *exe_file = NULL;
const struct path *dpath = NULL;
char *path = NULL, *buff = NULL;
char *filename = NULL;
exe_file = defex_get_source_file(p);
if (IS_ERR_OR_NULL(exe_file))
goto out_filename;
dpath = &exe_file->f_path;
if (!dpath->dentry || !dpath->dentry->d_inode) {
fput(exe_file);
goto out_filename;
}
path_get(dpath);
buff = kmalloc(PATH_MAX, GFP_KERNEL);
if (buff)
path = d_path(dpath, buff, PATH_MAX);
path_put(dpath);
#ifndef DEFEX_CACHES_ENABLE
fput(exe_file);
#endif /* DEFEX_CACHES_ENABLE */
out_filename:
if (path && !IS_ERR(path))
filename = kstrdup(path, GFP_KERNEL);
if (!filename)
filename = (char *)unknown_file;
if (buff)
kfree(buff);
return filename;
}
/* Resolve the filename to absolute path, follow the links
name - input file name
out_buff - output pointer to the allocated buffer (should be freed)
Returns: pointer to resolved filename or NULL
*/
char* defex_resolve_filename(const char *name, char **out_buff)
{
char *target_file = NULL, *buff = NULL;
struct path path;
if (*out_buff)
buff = *out_buff;
else
buff = kmalloc(PATH_MAX, GFP_KERNEL);
if (buff) {
if (!kern_path(name, LOOKUP_FOLLOW, &path)) {
target_file = d_path(&path, buff, PATH_MAX);
path_put(&path);
}
if (IS_ERR_OR_NULL(target_file)) {
kfree(buff);
buff = NULL;
target_file = NULL;
}
}
*out_buff = buff;
return target_file;
}
int defex_files_identical(const struct file *f1, const struct file *f2)
{
const struct path *dpath1, *dpath2;
const struct inode *inode1, *inode2;
if (f1 && f2) {
dpath1 = &f1->f_path;
dpath2 = &f2->f_path;
if (dpath1->dentry && dpath2->dentry) {
inode1 = dpath1->dentry->d_inode;
inode2 = dpath2->dentry->d_inode;
return (inode1 == inode2);
}
}
return 0;
}

View file

@ -1,76 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/defex.h>
#include "include/defex_internal.h"
int defex_get_features(void)
{
int features = 0;
#ifdef DEFEX_PED_ENABLE
#if !defined(DEFEX_PERMISSIVE_PED)
features |= GLOBAL_PED_STATUS;
#else
if (global_privesc_status != 0)
features |= FEATURE_CHECK_CREDS;
if (global_privesc_status == 2)
features |= FEATURE_CHECK_CREDS_SOFT;
#endif /* DEFEX_PERMISSIVE_PED */
#endif /* DEFEX_PED_ENABLE */
#ifdef DEFEX_INTEGRITY_ENABLE
#if !defined(DEFEX_PERMISSIVE_INT)
features |= GLOBAL_INTEGRITY_STATUS;
#else
if (global_integrity_status != 0)
features |= FEATURE_INTEGRITY;
if (global_integrity_status == 2)
features |= FEATURE_INTEGRITY_SOFT;
#endif /* DEFEX_PERMISSIVE_INT */
#endif /* DEFEX_INTEGRITY_ENABLE */
#ifdef DEFEX_SAFEPLACE_ENABLE
#if !defined(DEFEX_PERMISSIVE_SP)
features |= GLOBAL_SAFEPLACE_STATUS;
#else
if (global_safeplace_status != 0)
features |= FEATURE_SAFEPLACE;
if (global_safeplace_status == 2)
features |= FEATURE_SAFEPLACE_SOFT;
#endif /* DEFEX_PERMISSIVE_SP */
#endif /* DEFEX_SAFEPLACE_ENABLE */
#ifdef DEFEX_TRUSTED_MAP_ENABLE
#if !defined(DEFEX_PERMISSIVE_TM)
features |= GLOBAL_TRUSTED_MAP_STATUS;
#else
if (global_trusted_map_status != 0)
features |= FEATURE_TRUSTED_MAP;
if (global_trusted_map_status & DEFEX_TM_PERMISSIVE_MODE)
features |= FEATURE_TRUSTED_MAP_SOFT;
#endif /* DEFEX_PERMISSIVE_TM */
#endif /* DEFEX_TRUSTED_MAP_ENABLE */
#ifdef DEFEX_IMMUTABLE_ENABLE
#if !defined(DEFEX_PERMISSIVE_IM)
features |= GLOBAL_IMMUTABLE_STATUS;
#else
if (global_immutable_status != 0)
features |= FEATURE_IMMUTABLE;
if (global_immutable_status == 2)
features |= FEATURE_IMMUTABLE_SOFT;
#endif /* DEFEX_PERMISSIVE_IM */
#endif /* DEFEX_IMMUTABLE_ENABLE */
return features;
}

View file

@ -1,162 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/syscalls.h>
#include <linux/unistd.h>
#include <linux/version.h>
#include "include/defex_caches.h"
#include "include/defex_catch_list.h"
#include "include/defex_debug.h"
#include "include/defex_internal.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
#include <linux/bootconfig.h>
#endif
MODULE_DESCRIPTION("Defex Linux Security Module");
bool boot_state_recovery __ro_after_init;
#ifdef DEFEX_DEPENDING_ON_OEMUNLOCK
bool boot_state_unlocked __ro_after_init;
int warranty_bit __ro_after_init;
#endif /* DEFEX_DEPENDING_ON_OEMUNLOCK */
asmlinkage int defex_syscall_enter(long int syscallno, struct pt_regs *regs);
asmlinkage int (* const defex_syscall_catch_enter)(long int syscallno, struct pt_regs *regs) = defex_syscall_enter;
static int defex_init_done __initdata;
asmlinkage int defex_syscall_enter(long int syscallno, struct pt_regs *regs)
{
long err;
const struct local_syscall_struct *item;
if (!current)
return 0;
#if !defined(__arm__) && defined(CONFIG_COMPAT)
if (regs->pstate & PSR_MODE32_BIT)
item = get_local_syscall_compat(syscallno);
else
#endif /* __arm__ && CONFIG_COMPAT */
item = get_local_syscall(syscallno);
if (!item)
return 0;
err = item->err_code;
if (err) {
if (task_defex_enforce(current, NULL, item->local_syscall))
return err;
}
return 0;
}
#ifdef DEFEX_DEPENDING_ON_OEMUNLOCK
__visible_for_testing int __init verifiedboot_state_setup(char *str)
{
static const char unlocked[] = "orange";
if (str && !strncmp(str, unlocked, sizeof(unlocked))) {
boot_state_unlocked = true;
defex_log_crit("Device is unlocked and DEFEX will be disabled");
}
return 0;
}
__visible_for_testing int __init warrantybit_setup(char *str)
{
if (get_option(&str, &warranty_bit))
defex_log_crit("Warranty bit setup");
return 0;
}
__setup("androidboot.verifiedbootstate=", verifiedboot_state_setup);
__setup("androidboot.warranty_bit=", warrantybit_setup);
#endif /* DEFEX_DEPENDING_ON_OEMUNLOCK */
__visible_for_testing int __init bootstate_recovery_setup(char *str)
{
if (str && *str == '2') {
boot_state_recovery = true;
defex_log_crit("Recovery mode setup");
}
return 0;
}
__setup("bootmode=", bootstate_recovery_setup);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
void __init defex_bootconfig_setup(void)
{
char *value;
#ifdef DEFEX_DEPENDING_ON_OEMUNLOCK
value = (char *)xbc_find_value("androidboot.verifiedbootstate", NULL);
verifiedboot_state_setup(value);
value = (char *)xbc_find_value("androidboot.warranty_bit", NULL);
warrantybit_setup(value);
#endif /* DEFEX_DEPENDING_ON_OEMUNLOCK */
value = (char *)xbc_find_value("androidboot.boot_recovery", NULL);
bootstate_recovery_setup(value);
}
#endif /* LINUX_VERSION_CODE */
//INIT/////////////////////////////////////////////////////////////////////////
__visible_for_testing int __init defex_lsm_init(void)
{
#ifdef DEFEX_DEBUG_ENABLE
int ret;
#endif /* DEFEX_DEBUG_ENABLE */
#ifdef DEFEX_CACHES_ENABLE
defex_file_cache_init();
#endif /* DEFEX_CACHES_ENABLE */
#ifdef DEFEX_PED_ENABLE
creds_fast_hash_init();
#endif /* DEFEX_PED_ENABLE */
#ifdef DEFEX_DEBUG_ENABLE
ret = defex_init_sysfs();
if (ret) {
defex_log_crit("LSM defex_init_sysfs() failed!");
return ret;
}
#endif /* DEFEX_DEBUG_ENABLE */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
defex_bootconfig_setup();
#endif /* LINUX_VERSION_CODE */
defex_log_info("LSM started");
#ifdef DEFEX_LP_ENABLE
defex_log_info("ADB LP Enabled");
#endif /* DEFEX_LP_ENABLE */
defex_init_done = 1;
return 0;
}
__visible_for_testing int __init defex_lsm_load(void)
{
if (!boot_state_unlocked && defex_init_done)
do_load_rules();
return 0;
}
module_init(defex_lsm_init);
late_initcall(defex_lsm_load);

View file

@ -1,733 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <asm/barrier.h>
#include <linux/binfmts.h>
#include <linux/compiler.h>
#include <linux/const.h>
#ifdef DEFEX_DSMS_ENABLE
#include <linux/dsms.h>
#endif
#include <linux/file.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/namei.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/stddef.h>
#ifdef DEFEX_DSMS_ENABLE
#include <linux/string.h>
#endif
#include <linux/syscalls.h>
#include <linux/unistd.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include "include/defex_caches.h"
#include "include/defex_catch_list.h"
#include "include/defex_config.h"
#include "include/defex_debug.h"
#include "include/defex_internal.h"
#include "include/defex_rules.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/mm.h>
#include <linux/sched/task.h>
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
#define is_task_used(tsk) refcount_read(&(tsk)->usage)
#else
#define is_task_used(tsk) atomic_read(&(tsk)->usage)
#endif
__visible_for_testing struct task_struct *get_parent_task(const struct task_struct *p)
{
struct task_struct *parent = NULL;
read_lock(&tasklist_lock);
parent = p->parent;
if (parent)
get_task_struct(parent);
read_unlock(&tasklist_lock);
return parent;
}
#ifdef DEFEX_DSMS_ENABLE
# define PED_VIOLATION "DFX1"
# define SAFEPLACE_VIOLATION "DFX2"
# define INTEGRITY_VIOLATION "DFX3"
# define IMMUTABLE_VIOLATION "DFX4"
# define MESSAGE_BUFFER_SIZE 200
# define STORED_CREDS_SIZE 100
__visible_for_testing void defex_report_violation(const char *violation, uint64_t counter,
struct defex_context *dc, uid_t stored_uid, uid_t stored_fsuid, uid_t stored_egid, int case_num)
{
int usermode_result;
char message[MESSAGE_BUFFER_SIZE + 1];
struct task_struct *parent = NULL, *p = dc->task;
const uid_t uid = uid_get_value(dc->cred->uid);
const uid_t euid = uid_get_value(dc->cred->euid);
const uid_t fsuid = uid_get_value(dc->cred->fsuid);
const uid_t egid = uid_get_value(dc->cred->egid);
const char *process_name = p->comm;
const char *prt_process_name = NULL;
const char *program_path = get_dc_process_name(dc);
char *prt_program_path = NULL;
char *file_path = NULL;
char stored_creds[STORED_CREDS_SIZE + 1];
parent = get_parent_task(p);
if (!parent)
return;
prt_process_name = parent->comm;
prt_program_path = defex_get_filename(parent);
if (dc->target_file && !case_num) {
file_path = get_dc_target_name(dc);
} else {
snprintf(stored_creds, sizeof(stored_creds),
"[%ld, %ld, %ld]", (long)stored_uid, (long)stored_fsuid, (long)stored_egid);
stored_creds[sizeof(stored_creds) - 1] = 0;
}
snprintf(message, sizeof(message), "%d, %d, sc=%d, tsk=%s(%s), %s(%s), [%ld %ld %ld %ld], %s%s, %d",
warranty_bit, boot_state_unlocked, dc->syscall_no, process_name, program_path, prt_process_name,
prt_program_path, (long)uid, (long)euid, (long)fsuid, (long)egid,
(file_path ? "file=" : "stored "), (file_path ? file_path : stored_creds), case_num);
message[sizeof(message) - 1] = 0;
usermode_result = dsms_send_message(violation, message, counter);
#ifdef DEFEX_DEBUG_ENABLE
defex_log_err("Violation : feature=%s value=%ld, detail=[%s]", violation, (long)counter, message);
defex_log_err("Result : %d", usermode_result);
#endif /* DEFEX_DEBUG_ENABLE */
safe_str_free(prt_program_path);
put_task_struct(parent);
}
#endif /* DEFEX_DSMS_ENABLE */
#if defined(DEFEX_SAFEPLACE_ENABLE) || defined(DEFEX_TRUSTED_MAP_ENABLE) || defined(DEFEX_INTEGRITY_ENABLE)
__visible_for_testing long kill_process(struct task_struct *p)
{
read_lock(&tasklist_lock);
send_sig(SIGKILL, p, 0);
read_unlock(&tasklist_lock);
return 0;
}
#endif /* DEFEX_SAFEPLACE_ENABLE || DEFEX_TRUSTED_MAP_ENABLE || DEFEX_INTEGRITY_ENABLE */
#ifdef DEFEX_PED_ENABLE
__visible_for_testing long kill_process_group(struct task_struct *p, int tgid, int pid)
{
read_lock(&tasklist_lock);
for_each_process(p) {
if (p->tgid == tgid)
send_sig(SIGKILL, p, 0);
}
send_sig(SIGKILL, current, 0);
read_unlock(&tasklist_lock);
return 0;
}
__visible_for_testing int check_incfs(struct defex_context *dc)
{
char *new_file;
struct file *f = dc->target_file;
static const char incfs_path[] = "/data/incremental/";
if (f) {
new_file = get_dc_target_name(dc);
if (!strncmp(new_file, incfs_path, sizeof(incfs_path) - 1)) {
#ifdef DEFEX_DEBUG_ENABLE
defex_log_crit("Allow IncFS access");
#endif /* DEFEX_DEBUG_ENABLE */
return 1;
}
}
return 0;
}
__visible_for_testing int task_defex_is_secured(struct defex_context *dc)
{
struct file *exe_file = get_dc_process_file(dc);
char *proc_name = get_dc_process_name(dc);
int is_secured = 0;
if (!get_dc_process_dpath(dc))
return is_secured;
is_secured = !rules_lookup(proc_name, feature_ped_exception, exe_file);
return is_secured;
}
__visible_for_testing int at_same_group(unsigned int uid1, unsigned int uid2)
{
static const unsigned int lod_base = 0x61A8;
/* allow the weaken privilege */
if (uid1 >= 10000 && uid2 < 10000) return 1;
/* allow traverse in the same class */
if ((uid1 / 1000) == (uid2 / 1000)) return 1;
/* allow traverse to isolated ranges */
if (uid1 >= 90000) return 1;
/* allow LoD process */
return ((uid1 >> 16) == lod_base) && ((uid2 >> 16) == lod_base);
}
__visible_for_testing int at_same_group_gid(unsigned int gid1, unsigned int gid2)
{
static const unsigned int lod_base = 0x61A8, inet = 3003;
/* allow the weaken privilege */
if (gid1 >= 10000 && gid2 < 10000) return 1;
/* allow traverse in the same class */
if ((gid1 / 1000) == (gid2 / 1000)) return 1;
/* allow traverse to isolated ranges */
if (gid1 >= 90000) return 1;
/* allow LoD process */
return (((gid1 >> 16) == lod_base) || (gid1 == inet)) && ((gid2 >> 16) == lod_base);
}
#ifdef DEFEX_LP_ENABLE
/* Lower Permission feature decision function */
__visible_for_testing int lower_adb_permission(struct defex_context *dc, unsigned short cred_flags)
{
char *parent_file;
struct task_struct *parent = NULL, *p = dc->task;
#ifndef DEFEX_PERMISSIVE_LP
struct cred *shellcred;
static const char adbd_str[] = "/apex/com.android.adbd/bin/adbd";
#endif /* DEFEX_PERMISSIVE_LP */
int ret = 0;
parent = get_parent_task(p);
if (!parent || p->pid == 1 || parent->pid == 1)
goto out;
parent_file = defex_get_filename(parent);
#ifndef DEFEX_PERMISSIVE_LP
if (!strncmp(parent_file, adbd_str, sizeof(adbd_str))) {
shellcred = prepare_creds();
defex_log_crit("ADB with root");
if (!shellcred) {
defex_log_crit("Prepare_creds fail");
ret = 0;
goto out;
}
uid_set_value(shellcred->uid, 2000);
uid_set_value(shellcred->suid, 2000);
uid_set_value(shellcred->euid, 2000);
uid_set_value(shellcred->fsuid, 2000);
uid_set_value(shellcred->gid, 2000);
uid_set_value(shellcred->sgid, 2000);
uid_set_value(shellcred->egid, 2000);
uid_set_value(shellcred->fsgid, 2000);
commit_creds(shellcred);
dc->cred = (struct cred *)current_cred(); //shellcred;
set_task_creds(p, 2000, 2000, 2000, cred_flags);
ret = 1;
}
#endif /* DEFEX_PERMISSIVE_LP */
safe_str_free(parent_file);
out:
if (parent)
put_task_struct(parent);
return ret;
}
#endif /* DEFEX_LP_ENABLE */
/* Cred. violation feature decision function */
#define AID_MEDIA_RW 1023
#define AID_MEDIA_OBB 1059
#define AID_SYSTEM 1000
__visible_for_testing int task_defex_check_creds(struct defex_context *dc)
{
char *path = NULL;
int check_deeper, case_num;
unsigned int cur_uid, cur_euid, cur_fsuid, cur_egid;
unsigned int ref_uid, ref_fsuid, ref_egid;
struct task_struct *parent, *p = dc->task;
unsigned short cred_flags;
const struct cred *parent_cred;
static const unsigned int dead_uid = 0xDEADBEAF;
if (!is_task_creds_ready() || !p->cred)
goto out;
get_task_creds(p, &ref_uid, &ref_fsuid, &ref_egid, &cred_flags);
cur_uid = uid_get_value(dc->cred->uid);
cur_euid = uid_get_value(dc->cred->euid);
cur_fsuid = uid_get_value(dc->cred->fsuid);
cur_egid = uid_get_value(dc->cred->egid);
if (!ref_uid) {
if (p->tgid != p->pid && p->tgid != 1) {
path = get_dc_process_name(dc);
defex_log_crit("[6]: cred wasn't stored [task=%s, filename=%s, uid=%d, tgid=%u, pid=%u, ppid=%u]",
p->comm, path, cur_uid, p->tgid, p->pid, p->real_parent->pid);
defex_log_crit("[6]: stored [euid=%d fsuid=%d egid=%d] current [uid=%d euid=%d fsuid=%d egid=%d]",
ref_uid, ref_fsuid, ref_egid, cur_uid, cur_euid, cur_fsuid, cur_egid);
goto exit;
}
parent = get_parent_task(p);
if (parent) {
parent_cred = get_task_cred(parent);
if (CHECK_ROOT_CREDS(parent_cred))
cred_flags |= CRED_FLAGS_PROOT;
put_cred(parent_cred);
put_task_struct(parent);
}
if (CHECK_ROOT_CREDS(dc->cred)) {
#ifdef DEFEX_LP_ENABLE
if (!lower_adb_permission(dc, cred_flags))
#endif /* DEFEX_LP_ENABLE */
{
set_task_creds(p, 1, 1, 1, cred_flags);
}
}
else
set_task_creds(p, cur_euid, cur_fsuid, cur_egid, cred_flags);
} else if (ref_uid == 1) {
if (!CHECK_ROOT_CREDS(dc->cred))
set_task_creds(p, cur_euid, cur_fsuid, cur_egid, cred_flags);
} else if (ref_uid == dead_uid) {
path = get_dc_process_name(dc);
defex_log_crit("[5]: process wasn't killed [task=%s, filename=%s, uid=%d, tgid=%u, pid=%u, ppid=%u]",
p->comm, path, cur_uid, p->tgid, p->pid, p->real_parent->pid);
defex_log_crit("[5]: stored [euid=%d fsuid=%d egid=%d] current [uid=%d euid=%d fsuid=%d egid=%d]",
ref_uid, ref_fsuid, ref_egid, cur_uid, cur_euid, cur_fsuid, cur_egid);
goto exit;
} else {
check_deeper = 0;
/* temporary allow fsuid changes to "media_rw" */
if ( (cur_uid != ref_uid) ||
(cur_euid != ref_uid) ||
(cur_egid != ref_egid) ||
!((cur_fsuid == ref_fsuid) ||
(cur_fsuid == ref_uid) ||
(cur_fsuid%100000 == AID_SYSTEM) ||
(cur_fsuid%100000 == AID_MEDIA_RW) ||
(cur_fsuid%100000 == AID_MEDIA_OBB)) ) {
check_deeper = 1;
if (CHECK_ROOT_CREDS(dc->cred))
set_task_creds(p, 1, 1, 1, cred_flags);
else
set_task_creds(p, cur_euid, cur_fsuid, cur_egid, cred_flags);
}
if (check_deeper &&
(!at_same_group(cur_uid, ref_uid) ||
!at_same_group(cur_euid, ref_uid) ||
!at_same_group_gid(cur_egid, ref_egid) ||
!at_same_group(cur_fsuid, ref_fsuid)) &&
task_defex_is_secured(dc)) {
case_num = ((p->tgid == p->pid) ? 1 : 2);
goto trigger_violation;
}
}
if (CHECK_ROOT_CREDS(dc->cred) && !(cred_flags & CRED_FLAGS_PROOT) && task_defex_is_secured(dc)) {
if (p->tgid != p->pid) {
case_num = 3;
goto trigger_violation;
}
case_num = 4;
goto trigger_violation;
}
out:
return DEFEX_ALLOW;
trigger_violation:
if (check_incfs(dc))
return DEFEX_ALLOW;
set_task_creds(p, dead_uid, dead_uid, dead_uid, cred_flags);
path = get_dc_process_name(dc);
defex_log_crit("[%d]: credential violation [task=%s, filename=%s, uid=%d, tgid=%u, pid=%u, ppid=%u]",
case_num, p->comm, path, cur_uid, p->tgid, p->pid, p->real_parent->pid);
defex_log_crit("[%d]: stored [euid=%d fsuid=%d egid=%d] current [uid=%d euid=%d fsuid=%d egid=%d]",
case_num, ref_uid, ref_fsuid, ref_egid, cur_uid, cur_euid, cur_fsuid, cur_egid);
#ifdef DEFEX_DSMS_ENABLE
defex_report_violation(PED_VIOLATION, 0, dc, ref_uid, ref_fsuid, ref_egid, case_num);
#endif /* DEFEX_DSMS_ENABLE */
exit:
return -DEFEX_DENY;
}
#endif /* DEFEX_PED_ENABLE */
#ifdef DEFEX_INTEGRITY_ENABLE
__visible_for_testing int task_defex_integrity(struct defex_context *dc)
{
int ret = DEFEX_ALLOW, is_violation = 0;
char *proc_file, *new_file;
struct task_struct *p = dc->task;
if (!get_dc_target_dpath(dc))
goto out;
new_file = get_dc_target_name(dc);
is_violation = rules_lookup(new_file, feature_integrity_check, dc->target_file);
if (is_violation == DEFEX_INTEGRITY_FAIL) {
ret = -DEFEX_DENY;
proc_file = get_dc_process_name(dc);
defex_log_crit("Integrity violation [task=%s (%s), child=%s, uid=%d]",
p->comm, proc_file, new_file, uid_get_value(dc->cred->uid));
#ifdef DEFEX_DSMS_ENABLE
defex_report_violation(INTEGRITY_VIOLATION, 0, dc, 0, 0, 0, 0);
#endif /* DEFEX_DSMS_ENABLE */
}
out:
return ret;
}
#endif /* DEFEX_INTEGRITY_ENABLE */
#ifdef DEFEX_SAFEPLACE_ENABLE
/* Safeplace feature decision function */
__visible_for_testing int task_defex_safeplace(struct defex_context *dc)
{
int ret = DEFEX_ALLOW, is_violation = 0;
char *proc_file, *new_file;
struct task_struct *p = dc->task;
if (!CHECK_ROOT_CREDS(dc->cred))
goto out;
if (!get_dc_target_dpath(dc))
goto out;
new_file = get_dc_target_name(dc);
is_violation = !rules_lookup(new_file, feature_safeplace_path, dc->target_file);
if (is_violation) {
ret = -DEFEX_DENY;
proc_file = get_dc_process_name(dc);
defex_log_crit("Safeplace violation [task=%s (%s), child=%s, uid=%d]",
p->comm, proc_file, new_file, uid_get_value(dc->cred->uid));
#ifdef DEFEX_DSMS_ENABLE
defex_report_violation(SAFEPLACE_VIOLATION, 0, dc, 0, 0, 0, 0);
#endif /* DEFEX_DSMS_ENABLE */
}
out:
return ret;
}
#endif /* DEFEX_SAFEPLACE_ENABLE */
#ifdef DEFEX_TRUSTED_MAP_ENABLE
/* Trusted map feature decision function */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
__visible_for_testing int task_defex_trusted_map(struct defex_context *dc, va_list ap)
{
int ret = DEFEX_ALLOW, argc;
struct linux_binprm *bprm;
if (!CHECK_ROOT_CREDS(&dc->cred))
goto out;
bprm = va_arg(ap, struct linux_binprm *);
argc = bprm->argc;
#ifdef DEFEX_DEBUG_ENABLE
if (argc <= 0)
defex_log_crit("[DTM] Invalid trusted map arguments - check integration on fs/exec.c (argc %d)", argc);
#endif
ret = defex_trusted_map_lookup(dc, argc, bprm);
if (defex_tm_mode_enabled(DEFEX_TM_PERMISSIVE_MODE))
ret = DEFEX_ALLOW;
out:
return ret;
}
#else
__visible_for_testing int task_defex_trusted_map(struct defex_context *dc, va_list ap)
{
int ret = DEFEX_ALLOW, argc;
void *argv;
if (!CHECK_ROOT_CREDS(dc->cred))
goto out;
argc = va_arg(ap, int);
argv = va_arg(ap, void *);
#ifdef DEFEX_DEBUG_ENABLE
if (argc <= 0)
defex_log_crit(
"[DTM] Invalid trusted map arguments - check integration on fs/exec.c (argc %d)", argc);
#endif
ret = defex_trusted_map_lookup(dc, argc, argv);
if (defex_tm_mode_enabled(DEFEX_TM_PERMISSIVE_MODE))
ret = DEFEX_ALLOW;
out:
return ret;
}
#endif
#endif /* DEFEX_TRUSTED_MAP_ENABLE */
#ifdef DEFEX_IMMUTABLE_ENABLE
/* Immutable feature decision function */
__visible_for_testing int task_defex_src_exception(struct defex_context *dc)
{
struct file *exe_file = get_dc_process_file(dc);
char *proc_name = get_dc_process_name(dc);
int allow = 1;
if (!get_dc_process_dpath(dc))
return allow;
exe_file = get_dc_process_file(dc);
allow = rules_lookup(proc_name, feature_immutable_src_exception, exe_file);
return allow;
}
/* Immutable feature decision function */
__visible_for_testing int task_defex_immutable(struct defex_context *dc, int attribute)
{
int ret = DEFEX_ALLOW, is_violation = 0;
char *proc_file, *new_file;
struct task_struct *p = dc->task;
if (!get_dc_target_dpath(dc))
goto out;
new_file = get_dc_target_name(dc);
is_violation = rules_lookup(new_file, attribute, dc->target_file);
if (is_violation) {
/* Check the Source exception and self-access */
if (attribute == feature_immutable_path_open &&
(task_defex_src_exception(dc) ||
defex_files_identical(get_dc_process_file(dc), dc->target_file)))
goto out;
ret = -DEFEX_DENY;
proc_file = get_dc_process_name(dc);
defex_log_crit("Immutable %s violation [task=%s (%s), access to:%s]",
(attribute==feature_immutable_path_open)?"open":"write", p->comm, proc_file, new_file);
#ifdef DEFEX_DSMS_ENABLE
defex_report_violation(IMMUTABLE_VIOLATION, 0, dc, 0, 0, 0, 0);
#endif /* DEFEX_DSMS_ENABLE */
}
out:
return ret;
}
#endif /* DEFEX_IMMUTABLE_ENABLE */
/* Main decision function */
int task_defex_enforce(struct task_struct *p, struct file *f, int syscall, ...)
{
int ret = DEFEX_ALLOW;
int feature_flag;
const struct local_syscall_struct *item;
struct defex_context dc;
#ifdef DEFEX_TRUSTED_MAP_ENABLE
va_list ap;
#endif
if (boot_state_unlocked)
return ret;
if (!p || p->pid == 1 || !p->mm || !is_task_used(p))
return ret;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0))
if ((p->state & (__TASK_STOPPED | TASK_DEAD)) || (p->exit_state & (EXIT_ZOMBIE | EXIT_DEAD)))
#else
if ((p->__state & (__TASK_STOPPED | TASK_DEAD)) || (p->exit_state & (EXIT_ZOMBIE | EXIT_DEAD)))
#endif
return ret;
if (syscall < 0) {
item = get_local_syscall(-syscall);
if (!item)
return ret;
syscall = item->local_syscall;
}
feature_flag = defex_get_features();
get_task_struct(p);
if (!init_defex_context(&dc, syscall, p, f))
goto do_allow;
#ifdef DEFEX_PED_ENABLE
/* Credential escalation feature */
if (feature_flag & FEATURE_CHECK_CREDS) {
ret = task_defex_check_creds(&dc);
if (ret) {
if (!(feature_flag & FEATURE_CHECK_CREDS_SOFT)) {
release_defex_context(&dc);
put_task_struct(p);
kill_process_group(p, p->tgid, p->pid);
return -DEFEX_DENY;
}
}
}
#endif /* DEFEX_PED_ENABLE */
#ifdef DEFEX_INTEGRITY_ENABLE
/* Integrity feature */
if (feature_flag & FEATURE_INTEGRITY) {
if (syscall == __DEFEX_execve) {
ret = task_defex_integrity(&dc);
if (ret == -DEFEX_DENY) {
if (!(feature_flag & FEATURE_INTEGRITY_SOFT)) {
release_defex_context(&dc);
put_task_struct(p);
kill_process(p);
return -DEFEX_DENY;
}
}
}
}
#endif /* DEFEX_INTEGRITY_ENABLE */
#ifdef DEFEX_SAFEPLACE_ENABLE
/* Safeplace feature */
if (feature_flag & FEATURE_SAFEPLACE) {
if (syscall == __DEFEX_execve) {
ret = task_defex_safeplace(&dc);
if (ret == -DEFEX_DENY) {
if (!(feature_flag & FEATURE_SAFEPLACE_SOFT)) {
release_defex_context(&dc);
put_task_struct(p);
kill_process(p);
return -DEFEX_DENY;
}
}
}
}
#endif /* DEFEX_SAFEPLACE_ENABLE */
#ifdef DEFEX_IMMUTABLE_ENABLE
/* Immutable feature */
if (feature_flag & FEATURE_IMMUTABLE) {
if (syscall == __DEFEX_openat || syscall == __DEFEX_write) {
ret = task_defex_immutable(&dc,
(syscall == __DEFEX_openat)?feature_immutable_path_open:feature_immutable_path_write);
if (ret == -DEFEX_DENY) {
if (!(feature_flag & FEATURE_IMMUTABLE_SOFT)) {
goto do_deny;
}
}
}
}
#endif /* DEFEX_IMMUTABLE_ENABLE */
#ifdef DEFEX_TRUSTED_MAP_ENABLE
/* Trusted map feature */
if (feature_flag & FEATURE_TRUSTED_MAP) {
if (syscall == __DEFEX_execve) {
va_start(ap, syscall);
ret = task_defex_trusted_map(&dc, ap);
va_end(ap);
if (ret == -DEFEX_DENY) {
if (!(feature_flag & FEATURE_TRUSTED_MAP_SOFT)) {
kill_process(p);
goto do_deny;
}
}
}
}
#endif /* DEFEX_TRUSTED_MAP_ENABLE */
do_allow:
release_defex_context(&dc);
put_task_struct(p);
return DEFEX_ALLOW;
#if defined(DEFEX_IMMUTABLE_ENABLE) || defined(DEFEX_TRUSTED_MAP_ENABLE)
do_deny:
release_defex_context(&dc);
put_task_struct(p);
return -DEFEX_DENY;
#endif /* DEFEX_IMMUTABLE_ENABLE || DEFEX_TRUSTED_MAP_ENABLE */
}
int task_defex_zero_creds(struct task_struct *tsk)
{
int is_fork = -1;
if (tsk->flags & (PF_KTHREAD | PF_WQ_WORKER)) {
return 0;
}
if (is_task_creds_ready()) {
is_fork = ((tsk->flags & PF_FORKNOEXEC) && (!tsk->on_rq));
#ifdef TASK_NEW
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0))
if (!is_fork && (tsk->state & TASK_NEW))
#else
if (!is_fork && (tsk->__state & TASK_NEW))
#endif
is_fork = 1;
#endif /* TASK_NEW */
set_task_creds_tcnt(tsk, is_fork?1:-1);
}
#ifdef DEFEX_CACHES_ENABLE
defex_file_cache_delete(tsk->pid);
#endif /* DEFEX_CACHES_ENABLE */
return 0;
}
int task_defex_user_exec(const char *new_file)
{
#ifdef DEFEX_UMH_RESTRICTION_ENABLE
int res = DEFEX_ALLOW, is_violation;
struct file *fp = NULL;
static unsigned int rules_load_cnt;
if (boot_state_unlocked)
return DEFEX_ALLOW;
if (!check_rules_ready()) {
if (rules_load_cnt++%100 == 0)
defex_log_warn("Rules not ready");
goto umh_out;
}
if (current == NULL || current->fs == NULL) {
goto umh_out;
}
fp = local_fopen(new_file, O_RDONLY, 0);
if (IS_ERR(fp) || (fp == NULL)) {
res = DEFEX_DENY;
goto umh_out;
} else {
filp_close(fp, NULL);
}
is_violation = !rules_lookup(new_file, feature_umhbin_path, NULL);
if (is_violation) {
defex_log_warn("UMH Exec Denied: %s", new_file);
res = DEFEX_DENY;
goto umh_out;
}
umh_out:
return res;
#else
return DEFEX_ALLOW;
#endif /* DEFEX_UMH_RESTRICTION_ENABLE */
}

View file

@ -1,603 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/kobject.h>
#include <linux/types.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
#include <linux/mm.h>
#endif /* < KERNEL_VERSION(4, 12, 0) */
#include "include/defex_debug.h"
#include "include/defex_internal.h"
#include "include/defex_rules.h"
#include "include/defex_sign.h"
#ifdef DEFEX_TRUSTED_MAP_ENABLE
#include "include/defex_tailer.h"
#include "include/ptree.h"
#endif
#define LOAD_FLAG_DPOLICY 0x01
#define LOAD_FLAG_DPOLICY_SYSTEM 0x02
#define LOAD_FLAG_SYSTEM_FIRST 0x04
#define LOAD_FLAG_TIMEOUT 0x08
#define LOAD_FLAG_RECOVERY 0x10
#define DEFEX_RULES_ARRAY_SIZE_MIN 64
#define DEFEX_RULES_ARRAY_SIZE_FIXED (32 * 1024)
#define DEFEX_RULES_ARRAY_SIZE_MAX (256 * 1024)
/*
* Variant 1: Platform build, use static packed rules array
*/
#include "defex_packed_rules.inc"
#ifdef DEFEX_RAMDISK_ENABLE
/*
* Variant 2: Platform build, load rules from kernel ramdisk or system partition
*/
#ifdef DEFEX_SIGN_ENABLE
#include "include/defex_sign.h"
#endif
#if (DEFEX_RULES_ARRAY_SIZE < 8)
#undef DEFEX_RULES_ARRAY_SIZE
#define DEFEX_RULES_ARRAY_SIZE DEFEX_RULES_ARRAY_SIZE_MIN
#endif
#ifdef DEFEX_KERNEL_ONLY
#undef DEFEX_RULES_ARRAY_SIZE
#define DEFEX_RULES_ARRAY_SIZE DEFEX_RULES_ARRAY_SIZE_MAX
__visible_for_testing unsigned char packed_rules_primary[DEFEX_RULES_ARRAY_SIZE] = {0};
#else
#if (DEFEX_RULES_ARRAY_SIZE < DEFEX_RULES_ARRAY_SIZE_FIXED)
#undef DEFEX_RULES_ARRAY_SIZE
#define DEFEX_RULES_ARRAY_SIZE DEFEX_RULES_ARRAY_SIZE_FIXED
#endif
__visible_for_testing unsigned char packed_rules_primary[DEFEX_RULES_ARRAY_SIZE] __ro_after_init = {0};
#endif /* DEFEX_KERNEL_ONLY */
static unsigned char *packed_rules_secondary;
#ifdef DEFEX_TRUSTED_MAP_ENABLE
struct PPTree dtm_tree;
#endif
#endif /* DEFEX_RAMDISK_ENABLE */
#ifdef DEFEX_TRUSTED_MAP_ENABLE
/* In loaded policy, title of DTM's section; set by tailer -t in buildscript/build_external/defex. */
#define DEFEX_DTM_SECTION_NAME "dtm_rules"
#endif
#ifdef DEFEX_INTEGRITY_ENABLE
#include <linux/fs.h>
#include <crypto/hash.h>
#include <crypto/public_key.h>
#include <crypto/internal/rsa.h>
#include "../../integrity/integrity.h"
#define SHA256_DIGEST_SIZE 32
#endif /* DEFEX_INTEGRITY_ENABLE */
struct rules_file_struct {
char *name;
int flags;
};
static const struct rules_file_struct rules_files[4] = {
{ "/dpolicy", LOAD_FLAG_DPOLICY },
{ "/first_stage_ramdisk/dpolicy", LOAD_FLAG_DPOLICY },
{ "/vendor/etc/dpolicy", LOAD_FLAG_DPOLICY },
{ "/dpolicy_system", LOAD_FLAG_DPOLICY_SYSTEM }
};
static volatile unsigned int load_flags;
__visible_for_testing unsigned long get_current_sec(void)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
return get_seconds();
#else
return ktime_get_seconds();
#endif
}
__visible_for_testing char *get_rules_ptr(int is_system)
{
if (load_flags & LOAD_FLAG_SYSTEM_FIRST)
is_system = !is_system;
return is_system ? (char *)packed_rules_secondary : (char *)packed_rules_primary;
}
__visible_for_testing int get_rules_size(int is_system)
{
struct rule_item_struct *rules_ptr = (struct rule_item_struct *)get_rules_ptr(is_system);
return rules_ptr ? rules_ptr->data_size : 0;
}
int check_rules_ready(void)
{
struct rule_item_struct *base = (struct rule_item_struct *)packed_rules_primary;
return (!base || !base->data_size)?0:1;
}
__visible_for_testing int check_system_mount(void)
{
static int mount_system_root = -1;
struct file *fp;
if (mount_system_root < 0) {
fp = local_fopen("/sbin/recovery", O_RDONLY, 0);
if (IS_ERR(fp))
fp = local_fopen("/system/bin/recovery", O_RDONLY, 0);
if (!IS_ERR(fp)) {
defex_log_crit("Recovery mode");
filp_close(fp, NULL);
load_flags |= LOAD_FLAG_RECOVERY;
} else {
defex_log_crit("Normal mode");
}
mount_system_root = 0;
fp = local_fopen("/system_root", O_DIRECTORY | O_PATH, 0);
if (!IS_ERR(fp)) {
filp_close(fp, NULL);
mount_system_root = 1;
defex_log_crit("System_root=TRUE");
} else {
defex_log_crit("System_root=FALSE");
}
}
return (mount_system_root > 0);
}
#ifdef DEFEX_INTEGRITY_ENABLE
__visible_for_testing int defex_check_integrity(struct file *f, unsigned char *hash)
{
struct crypto_shash *handle = NULL;
struct shash_desc *shash = NULL;
static const unsigned char buff_zero[SHA256_DIGEST_SIZE] = {0};
unsigned char hash_sha256[SHA256_DIGEST_SIZE];
unsigned char *buff = NULL;
size_t buff_size = PAGE_SIZE;
loff_t file_size = 0;
int ret = 0, err = 0, read_size = 0;
// A saved hash is zero, skip integrity check
if (!memcmp(buff_zero, hash, SHA256_DIGEST_SIZE))
return ret;
if (IS_ERR(f))
goto hash_error;
handle = crypto_alloc_shash("sha256", 0, 0);
if (IS_ERR(handle)) {
err = PTR_ERR(handle);
defex_log_err("Can't alloc sha256, error : %d", err);
return -1;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
shash = (struct shash_desc *)kvzalloc(sizeof(struct shash_desc) + crypto_shash_descsize(handle), GFP_KERNEL);
#else
shash = kzalloc(sizeof(struct shash_desc) + crypto_shash_descsize(handle), GFP_KERNEL);
#endif /* < KERNEL_VERSION(4, 12, 0) */
if (shash == NULL)
goto hash_error;
shash->tfm = handle;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
buff = kvmalloc(buff_size, GFP_KERNEL);
#else
buff = kmalloc(buff_size, GFP_KERNEL);
#endif /* < KERNEL_VERSION(4, 12, 0) */
if (buff == NULL)
goto hash_error;
err = crypto_shash_init(shash);
if (err < 0)
goto hash_error;
while (1) {
read_size = local_fread(f, file_size, (char *)buff, buff_size);
if (read_size < 0)
goto hash_error;
if (read_size == 0)
break;
file_size += read_size;
err = crypto_shash_update(shash, buff, read_size);
if (err < 0)
goto hash_error;
}
err = crypto_shash_final(shash, hash_sha256);
if (err < 0)
goto hash_error;
ret = memcmp(hash_sha256, hash, SHA256_DIGEST_SIZE);
goto hash_exit;
hash_error:
ret = -1;
hash_exit:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
kvfree(buff);
kvfree(shash);
#else
kfree(buff);
kfree(shash);
#endif /* < KERNEL_VERSION(4, 12, 0) */
if (handle)
crypto_free_shash(handle);
return ret;
}
__visible_for_testing int defex_integrity_default(const char *file_path)
{
static const char integrity_default[] = "/system/bin/install-recovery.sh";
return strncmp(integrity_default, file_path, sizeof(integrity_default));
}
#endif /* DEFEX_INTEGRITY_ENABLE */
#if defined(DEFEX_RAMDISK_ENABLE)
#ifdef DEFEX_TRUSTED_MAP_ENABLE
static const unsigned char *find_policy_section(const char *name, const char *data, int data_size, long *section_size)
{
return data_size > 0 ? defex_tailerp_find(data, data_size, name, section_size) : 0;
}
#endif
__visible_for_testing int check_rule_structure(unsigned char *data_buff)
{
struct rule_item_struct *rules_ptr = (struct rule_item_struct *)data_buff;
int res = 0;
const int req_size = sizeof(struct rule_item_struct) + rules_ptr->size;
#ifdef DEFEX_INTEGRITY_ENABLE
const int integrity_state = 1;
#else
const int integrity_state = 0;
#endif /* DEFEX_INTEGRITY_ENABLE */
if (rules_ptr->next_level != req_size || memcmp(rules_ptr->name, "DEFEX_RULES_FILE", 16) != 0) {
defex_log_err("Rules structure is wrong. Integrity state: %d", integrity_state);
res = -1;
} else {
defex_log_info("Rules structure is OK. Integrity state: %d", integrity_state);
}
return res;
}
__visible_for_testing int load_rules_common(struct file *f, int flags)
{
int res = -1, data_size, rules_size;
unsigned char *data_buff = NULL;
data_size = i_size_read(file_inode(f));
if (data_size <= 0 || data_size > DEFEX_RULES_ARRAY_SIZE_MAX)
goto do_clean;
data_buff = vmalloc(data_size);
if (!data_buff)
goto do_clean;
rules_size = local_fread(f, 0, data_buff, data_size);
if (rules_size <= 0) {
defex_log_err("Failed to read rules file (%d)", rules_size);
goto do_clean;
}
defex_log_info("Read %d bytes", rules_size);
#ifdef DEFEX_SIGN_ENABLE
res = defex_rules_signature_check((char *)data_buff, (unsigned int)rules_size, (unsigned int *)&rules_size);
if (!res)
defex_log_info("Rules signature verified successfully");
else
defex_log_err("Rules signature incorrect!!!");
#else
res = 0;
#endif
if (!res)
res = check_rule_structure(data_buff);
if (!res) {
const unsigned char *policy_data = NULL; /* where additional features like DTM could look for policy data */
if (!(load_flags & (LOAD_FLAG_DPOLICY | LOAD_FLAG_DPOLICY_SYSTEM))) {
if (rules_size > sizeof(packed_rules_primary)) {
res = -1;
goto do_clean;
}
memcpy(packed_rules_primary, data_buff, rules_size);
policy_data = packed_rules_primary;
if (flags & LOAD_FLAG_DPOLICY_SYSTEM)
load_flags |= LOAD_FLAG_SYSTEM_FIRST;
defex_log_info("Primary rules have been stored");
} else {
if (rules_size > 0) {
policy_data = packed_rules_secondary = data_buff;
data_buff = NULL;
defex_log_info("Secondary rules have been stored");
}
}
#ifdef DEFEX_SHOW_RULES_ENABLE
if (policy_data)
defex_show_structure((void *)policy_data, rules_size);
#endif /* DEFEX_SHOW_RULES_ENABLE */
#ifdef DEFEX_TRUSTED_MAP_ENABLE
if (policy_data && !dtm_tree.data) { /* DTM not yet initialized */
const unsigned char *dtm_section = find_policy_section(DEFEX_DTM_SECTION_NAME, policy_data, rules_size, 0);
if (dtm_section)
pptree_set_data(&dtm_tree, dtm_section);
}
#endif
load_flags |= flags;
res = rules_size;
}
do_clean:
filp_close(f, NULL);
vfree(data_buff);
return res;
}
int load_rules_late(int forced_load)
{
struct file *f = NULL;
int f_index, res = 0;
static atomic_t load_lock = ATOMIC_INIT(1);
const struct rules_file_struct *item;
static unsigned long start_time;
static unsigned long last_time;
static int load_counter = 0;
unsigned long cur_time = get_current_sec();
if (load_flags & LOAD_FLAG_TIMEOUT)
return -1;
if (!atomic_dec_and_test(&load_lock)) {
atomic_inc(&load_lock);
return res;
}
/* The first try to load, initialize time values */
if (!start_time)
start_time = cur_time;
/* Skip this try, wait for next second */
if (!forced_load && (cur_time == last_time))
goto do_exit;
last_time = cur_time;
/* Load has been attempted for 20 seconds, give up. */
if ((cur_time - start_time) > 20) {
res = -1;
load_flags |= LOAD_FLAG_TIMEOUT;
defex_log_warn("Late load timeout. Try counter = %d", load_counter);
goto do_exit;
}
load_counter++;
for (f_index = 0; f_index < ARRAY_SIZE(rules_files); f_index++) {
item = &rules_files[f_index];
if (!(item->flags & load_flags)) {
f = local_fopen(item->name, O_RDONLY, 0);
if (!IS_ERR_OR_NULL(f)) {
defex_log_info("Late load rules file: %s", item->name);
break;
}
}
}
if (IS_ERR_OR_NULL(f)) {
#ifdef DEFEX_KERNEL_ONLY
defex_log_err("Failed to open rules file (%ld)", (long)PTR_ERR(f));
#endif /* DEFEX_KERNEL_ONLY */
goto do_exit;
}
res = load_rules_common(f, item->flags);
res = (res < 0) ? res : (res > 0);
do_exit:
atomic_inc(&load_lock);
return res;
}
int __init do_load_rules(void)
{
struct file *f = NULL;
int res = -1;
unsigned int f_index = 0;
const struct rules_file_struct *item;
if (boot_state_recovery)
load_flags |= LOAD_FLAG_RECOVERY;
load_next:
while (f_index < ARRAY_SIZE(rules_files)) {
item = &rules_files[f_index];
if (!(load_flags & item->flags)) {
f = local_fopen(item->name, O_RDONLY, 0);
if (!IS_ERR_OR_NULL(f)) {
defex_log_info("Load rules file: %s", item->name);
break;
}
}
f_index++;
};
if (f_index == ARRAY_SIZE(rules_files)) {
if (load_flags & (LOAD_FLAG_DPOLICY_SYSTEM | LOAD_FLAG_DPOLICY))
return 0;
defex_log_err("Failed to open rules file (%ld)", (long)PTR_ERR(f));
#ifdef DEFEX_KERNEL_ONLY
if (load_flags & LOAD_FLAG_RECOVERY)
res = 0;
#endif /* DEFEX_KERNEL_ONLY */
return res;
}
res = load_rules_common(f, item->flags);
res = (res < 0) ? res : 0;
#ifdef DEFEX_KERNEL_ONLY
if ((load_flags & LOAD_FLAG_RECOVERY) && res != 0) {
res = 0;
defex_log_info("Kernel Only & recovery mode, rules loading is passed");
}
#endif
if (++f_index < ARRAY_SIZE(rules_files))
goto load_next;
return res;
}
#endif /* DEFEX_RAMDISK_ENABLE */
__visible_for_testing struct rule_item_struct *lookup_dir(struct rule_item_struct *base,
const char *name,
int l,
int for_recovery,
char *base_start)
{
struct rule_item_struct *item = NULL;
unsigned int offset;
if (!base || !base->next_level || !base_start)
return item;
item = GET_ITEM_PTR(base->next_level, base_start);
do {
if ((!(item->feature_type & feature_is_file)
|| (!!(item->feature_type & feature_for_recovery)) == for_recovery)
&& item->size == l && !memcmp(name, item->name, l))
return item;
offset = item->next_file;
item = GET_ITEM_PTR(offset, base_start);
} while (offset);
return NULL;
}
__visible_for_testing int lookup_tree(const char *file_path, int attribute, struct file *f)
{
const char *ptr, *next_separator;
struct rule_item_struct *base, *cur_item = NULL;
char *base_start;
int l, is_system, forced_load;
const int is_recovery = !!(load_flags & LOAD_FLAG_RECOVERY);
const unsigned int load_both_mask = (LOAD_FLAG_DPOLICY | LOAD_FLAG_DPOLICY_SYSTEM);
int iterator = 0;
if (!file_path || *file_path != '/')
return 0;
is_system = ((strncmp("/system/", file_path, 8) == 0) ||
(strncmp("/product/", file_path, 9) == 0) ||
(strncmp("/apex/", file_path, 6) == 0) ||
(strncmp("/system_ext/", file_path, 12) == 0))?1:0;
forced_load = is_system &&
(attribute == feature_safeplace_path ||
attribute == feature_umhbin_path);
if ((load_flags & load_both_mask) != load_both_mask &&
!(load_flags & LOAD_FLAG_TIMEOUT)) {
/* allow all requests if rules were not loaded for Recovery mode */
if (!load_rules_late(forced_load) || is_recovery)
return (attribute == feature_ped_exception ||
attribute == feature_safeplace_path)?1:0;
}
try_not_system:
base_start = get_rules_ptr(get_rules_size(is_system) ? is_system : (!is_system));
base = (struct rule_item_struct *)base_start;
if (!base || !base->data_size) {
/* block all requests if rules were not loaded */
return 0;
}
ptr = file_path + 1;
do {
next_separator = strchr(ptr, '/');
if (!next_separator)
l = strlen(ptr);
else
l = next_separator - ptr;
if (!l)
return 0;
cur_item = lookup_dir(base, ptr, l, is_recovery, base_start);
if (!cur_item)
cur_item = lookup_dir(base, ptr, l, !is_recovery, base_start);
if (!cur_item)
break;
if (cur_item->feature_type & attribute) {
#ifdef DEFEX_INTEGRITY_ENABLE
/* Integrity acceptable only for files */
if ((cur_item->feature_type & feature_integrity_check) &&
(cur_item->feature_type & feature_is_file) && f) {
if (defex_integrity_default(file_path)
&& defex_check_integrity(f, cur_item->integrity))
return DEFEX_INTEGRITY_FAIL;
}
#endif /* DEFEX_INTEGRITY_ENABLE */
if (attribute & (feature_immutable_path_open | feature_immutable_path_write)
&& !(cur_item->feature_type & feature_is_file)) {
/* Allow open the folder by default */
if (!next_separator || *(ptr + l + 1) == 0)
return 0;
}
return 1;
}
base = cur_item;
ptr += l;
if (next_separator)
ptr++;
} while (*ptr);
if ((load_flags & load_both_mask) == load_both_mask && ++iterator < 2) {
is_system = !is_system;
goto try_not_system;
}
return 0;
}
int rules_lookup(const char *target_file, int attribute, struct file *f)
{
int ret = 0;
#if (defined(DEFEX_SAFEPLACE_ENABLE) || defined(DEFEX_IMMUTABLE_ENABLE) || defined(DEFEX_PED_ENABLE))
static const char system_root_txt[] = "/system_root";
if (check_system_mount() &&
!strncmp(target_file, system_root_txt, sizeof(system_root_txt) - 1))
target_file += (sizeof(system_root_txt) - 1);
ret = lookup_tree(target_file, attribute, f);
#endif
return ret;
}
void __init defex_load_rules(void)
{
#if defined(DEFEX_RAMDISK_ENABLE)
if (!boot_state_unlocked && do_load_rules() != 0) {
#if !(defined(DEFEX_DEBUG_ENABLE) || defined(DEFEX_KERNEL_ONLY))
panic("[DEFEX] Signature mismatch.\n");
#endif
}
#endif /* DEFEX_RAMDISK_ENABLE */
}

View file

@ -1,52 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/async.h>
#include <linux/delay.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/initrd.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/time.h>
#include <linux/version.h>
#include "include/defex_debug.h"
#include "include/defex_internal.h"
#include "include/defex_rules.h"
static struct kset *defex_kset;
int __init defex_init_sysfs(void)
{
defex_kset = kset_create_and_add("defex", NULL, NULL);
if (!defex_kset)
return -ENOMEM;
#if defined(DEFEX_DEBUG_ENABLE)
if (defex_create_debug(defex_kset) != DEFEX_OK)
goto kset_error;
#endif /* DEFEX_DEBUG_ENABLE */
return 0;
kset_error:
kset_unregister(defex_kset);
defex_kset = NULL;
return -ENOMEM;
}

View file

@ -1,98 +0,0 @@
/* Routines for handling archival-like files where each new contents is
* appended and linked backwards - memory-only variants.
*/
#include <linux/string.h>
#include "include/defex_tailer.h"
/* Reads int from 4-byte big-endian */
static int be2int(const unsigned char *p)
{
return (*p << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
}
long defex_tailerp_has_suffix(const unsigned char *p, long size)
{
return !p || size < TAIL_MAGIC_LEN + 8 + 1 + 2 ||
memcmp(p + size - TAIL_MAGIC_LEN, TAIL_MAGIC, TAIL_MAGIC_LEN)
? 0 : size - TAIL_MAGIC_LEN;
}
int defex_tailerp_iterate(const unsigned char *p, long size,
int (*task)(const char *title, int titleLen,
const unsigned char *start, long size,
void *data),
void *data)
{
long start, offset = defex_tailerp_has_suffix(p, size);
char buffer[TAIL_MAX_TITLE_LENGTH];
if (!offset)
return 0;
for (offset -= 2; ; offset = start - 2) {
int i, ttlLength;
long size;
/* Possibly change behavior depending on version
* (p[offset] and p[offset + 1])
*/
ttlLength = p[--offset];
if (offset - 4 - 4 - ttlLength < 0)
return -1;
memcpy(buffer, p + (offset -= ttlLength), ttlLength);
size = be2int(p + (offset -= 4));
start = be2int(p + (offset -= 4));
if (task) {
i = (*task)(buffer, ttlLength,
p + start, size, data);
if (i < 0)
return i;
}
if (!start)
break;
}
return 0;
}
/* Auxiliary data for finding an entry */
struct find_data {
const char *title;
int titleLen;
int found;
const unsigned char *start;
long size;
};
static int tailerp_iteratefind(const char *title, int titleLen,
const unsigned char *start, long size,
void *data)
{
struct find_data *fd = (struct find_data *)data;
if (fd->titleLen == titleLen &&
!memcmp(title, fd->title, titleLen)) {
fd->found = 1;
fd->start = start;
fd->size = size;
return -1;
}
return 0;
}
const unsigned char *defex_tailerp_find(const unsigned char *p, long size,
const char *title, long *sizep)
{
struct find_data fd;
fd.title = title;
fd.titleLen = strlen(title);
fd.found = 0;
if (!defex_tailerp_iterate(p, size, tailerp_iteratefind, &fd))
return 0;
if (fd.found) {
if (sizep)
*sizep = fd.size;
return fd.start;
}
return 0;
}

View file

@ -1,411 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/cred.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#ifdef DEFEX_LOG_BUFFER_ENABLE
#include <linux/ktime.h>
#include <linux/list.h>
#include <linux/rculist.h>
#endif /* DEFEX_LOG_BUFFER_ENABLE */
#include <linux/sched.h>
#include <linux/slab.h>
#ifdef DEFEX_LOG_BUFFER_ENABLE
#include <linux/spinlock.h>
#endif /* DEFEX_LOG_BUFFER_ENABLE */
#include <linux/string.h>
#include <linux/sysfs.h>
#include "include/defex_debug.h"
#include "include/defex_internal.h"
static int last_cmd;
#ifdef DEFEX_LOG_BUFFER_ENABLE
unsigned char *log_buf;
DEFINE_SPINLOCK(log_buf_lock);
static unsigned int log_buf_in_index, log_buf_out_index;
static unsigned long log_buf_size, log_buf_mask;
static int log_buf_init(void)
{
int ret = 0;
log_buf_size = DEFEX_LOG_BUF_SIZE;
do {
log_buf = vmalloc(log_buf_size);
if (log_buf)
break;
log_buf_size >>= 1;
} while (log_buf_size > PAGE_SIZE);
if (!log_buf)
ret = -1;
log_buf_in_index = 0;
log_buf_out_index = 0;
log_buf_mask = log_buf_size - 1;
defex_log_info("Log buffer size set to %ld bytes", log_buf_size);
return ret;
}
static int log_buffer_put(const char *msg, int msg_len)
{
char c;
int avail, ret = -1;
if (!log_buf_size)
return ret;
spin_lock(&log_buf_lock);
avail = log_buf_size - ((log_buf_in_index - log_buf_out_index) & log_buf_mask);
if (avail > msg_len) {
do {
c = *msg++;
log_buf[log_buf_in_index++] = c;
log_buf_in_index &= log_buf_mask;
} while (--msg_len);
if (c != '\n') {
log_buf[log_buf_in_index++] = '\n';
log_buf_in_index &= log_buf_mask;
}
ret = 0;
}
spin_unlock(&log_buf_lock);
return ret;
}
static int log_buffer_get(char *str, int buff_size)
{
char value;
int index = 0;
unsigned int log_buf_out_local;
if (log_buf_in_index == log_buf_out_index || buff_size < 2)
return 0;
spin_lock(&log_buf_lock);
log_buf_out_local = log_buf_out_index;
do {
value = log_buf[log_buf_out_local++];
str[index++] = value;
log_buf_out_local &= log_buf_mask;
if (value == '\n') {
str[index] = 0;
log_buf_out_index = log_buf_out_local;
spin_unlock(&log_buf_lock);
return index;
}
} while (--buff_size > 1 && (log_buf_in_index != log_buf_out_local));
spin_unlock(&log_buf_lock);
return 0;
}
void log_buffer_flush(void)
{
char msg[MAX_LEN];
int msg_len;
pr_info("%s== Buffer flushing begin ==", DEFEX_LOG_TAG);
do {
msg_len = log_buffer_get(&msg[0], MAX_LEN);
if (msg_len)
pr_info("%s", msg);
} while (msg_len);
pr_info("%s== Buffer flushing end ==", DEFEX_LOG_TAG);
}
#endif /* DEFEX_LOG_BUFFER_ENABLE */
void defex_print_msg(const enum defex_log_level msg_type, const char *format, ...)
{
char msg[MAX_LEN];
int msg_len, ktime_msg_len = 0;
va_list aptr;
static const char header[] = DEFEX_LOG_TAG;
#ifdef DEFEX_LOG_BUFFER_ENABLE
if ((msg_type != MSG_TIMEOFF) && (msg_type != MSG_BLOB)) {
ktime_t cur_time = ktime_get_boottime();
ktime_msg_len = snprintf(msg, MAX_LEN,
"[% 4lld.%04lld] ", cur_time/1000000000, (cur_time%1000000000)/100000);
}
#endif /* DEFEX_LOG_BUFFER_ENABLE */
va_start(aptr, format);
msg_len = vsnprintf(msg + ktime_msg_len, MAX_LEN - ktime_msg_len, format, aptr);
va_end(aptr);
#ifdef DEFEX_LOG_BUFFER_ENABLE
if (DEFEX_LOG_LEVEL_MASK & msg_type)
if (!log_buffer_put(msg, msg_len + ktime_msg_len))
return;
#endif /* DEFEX_LOG_BUFFER_ENABLE */
switch (msg_type) {
case MSG_CRIT:
pr_crit("%s%s\n", header, msg + ktime_msg_len);
break;
case MSG_ERR:
pr_err("%s%s\n", header, msg + ktime_msg_len);
break;
case MSG_WARN:
pr_warn("%s%s\n", header, msg + ktime_msg_len);
break;
case MSG_INFO:
case MSG_TIMEOFF:
pr_info("%s%s\n", header, msg + ktime_msg_len);
break;
case MSG_DEBUG:
pr_debug("%s%s\n", header, msg + ktime_msg_len);
break;
case MSG_BLOB:
pr_crit("%s\n", msg + ktime_msg_len);
break;
}
}
void blob(const char *title, const char *buffer, const size_t bufLen, const int lineSize)
{
size_t i = 0, line;
size_t j = 0, len = bufLen;
int offset = 0;
char c, stringToPrint[MAX_DATA_LEN];
defex_log_blob("%s", title);
do {
line = (len > lineSize)?lineSize:len;
offset = 0;
offset += snprintf(stringToPrint + offset, MAX_DATA_LEN - offset, "| 0x%08lX | ", i);
for (j = 0; j < line; j++)
offset += snprintf(stringToPrint + offset, MAX_DATA_LEN - offset, "%02X ",
(unsigned char)buffer[i + j]);
if (line < lineSize) {
for (j = 0; j < lineSize - line; j++)
offset += snprintf(stringToPrint + offset, MAX_DATA_LEN - offset, " ");
}
offset += snprintf(stringToPrint + offset, MAX_DATA_LEN - offset, "| ");
for (j = 0; j < line; j++) {
c = buffer[i + j];
c = (c < 0x20) || (c >= 0x7F)?'.':c;
offset += snprintf(stringToPrint + offset, MAX_DATA_LEN - offset, "%c", c);
}
if (line < lineSize) {
for (j = 0; j < lineSize - line; j++)
offset += snprintf(stringToPrint + offset, MAX_DATA_LEN - offset, " ");
}
offset += snprintf(stringToPrint + offset, MAX_DATA_LEN - offset, " |");
defex_log_blob("%s", stringToPrint);
memset(stringToPrint, 0, MAX_DATA_LEN);
i += line;
len -= line;
} while (len);
}
__visible_for_testing int set_user(struct cred *new_cred)
{
struct user_struct *new_user;
new_user = alloc_uid(new_cred->uid);
if (!new_user)
return -EAGAIN;
free_uid(new_cred->user);
new_cred->user = new_user;
return 0;
}
/*
* target_id = (0 - set all uids, 1 - set fsuid, 2 - set all gids)
*/
__visible_for_testing int set_cred(int target_id, int new_val)
{
struct user_namespace *ns = current_user_ns();
const struct cred *old_cred;
struct cred *new_cred;
kuid_t kuid;
kgid_t kgid;
uid_t new_uid;
gid_t new_gid;
new_cred = prepare_creds();
if (!new_cred)
return -ENOMEM;
old_cred = current_cred();
switch (target_id) {
case 0:
new_uid = new_val;
kuid = make_kuid(ns, new_uid);
if (!uid_valid(kuid))
goto do_abort;
new_cred->uid = new_cred->euid = new_cred->suid = new_cred->fsuid = kuid;
if (!uid_eq(new_cred->uid, old_cred->uid)) {
if (set_user(new_cred) < 0)
goto do_abort;
}
break;
case 1:
new_uid = new_val;
kuid = make_kuid(ns, new_uid);
if (!uid_valid(kuid))
goto do_abort;
new_cred->fsuid = kuid;
break;
case 2:
new_gid = new_val;
kgid = make_kgid(ns, new_gid);
if (!gid_valid(kgid))
goto do_abort;
new_cred->gid = new_cred->egid = new_cred->sgid = new_cred->fsgid = kgid;
break;
}
return commit_creds(new_cred);
do_abort:
abort_creds(new_cred);
return -EPERM;
}
__visible_for_testing ssize_t debug_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
struct task_struct *p = current;
int i, l, new_val = -1;
int ret = 0;
static const char *prefix[] = {
"uid=",
"fsuid=",
"gid=",
"pe_status=",
"im_status=",
"sp_status=",
"int_status=",
"get_log"
};
if (!buf || !p)
return -EINVAL;
for (i = 0; i < sizeof(prefix) / sizeof(prefix[0]); i++) {
l = strlen(prefix[i]);
if (!strncmp(buf, prefix[i], l))
break;
}
if (i == (sizeof(prefix) / sizeof(prefix[0])))
return -EINVAL;
switch (i) {
case DBG_SETUID ... DBG_SETGID:
ret = kstrtoint(buf + l, 10, &new_val);
if (ret != 0)
return -EINVAL;
ret = set_cred(i, new_val);
break;
case DBG_SET_PE_STATUS:
#ifdef DEFEX_PED_ENABLE
privesc_status_store(buf + l);
#endif /* DEFEX_PED_ENABLE */
break;
case DBG_SET_IM_STATUS:
#ifdef DEFEX_IMMUTABLE_ENABLE
immutable_status_store(buf + l);
#endif /* DEFEX_IMMUTABLE_ENABLE */
break;
case DBG_SET_SP_STATUS:
#ifdef DEFEX_SAFEPLACE_ENABLE
safeplace_status_store(buf + l);
#endif /* DEFEX_SAFEPLACE_ENABLE */
break;
case DBG_SET_INT_STATUS:
#ifdef DEFEX_INTEGRITY_ENABLE
integrity_status_store(buf + l);
#endif /* DEFEX_INTEGRITY_ENABLE */
break;
case DBG_GET_LOG:
break;
default:
break;
}
last_cmd = i;
return (!ret)?count:ret;
}
__visible_for_testing ssize_t debug_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
struct task_struct *p = current;
int res = 0;
#ifdef DEFEX_LOG_BUFFER_ENABLE
int buff_size, msg_len;
#endif /* DEFEX_LOG_BUFFER_ENABLE */
if (!p)
return 0;
switch (last_cmd) {
case DBG_SETUID ... DBG_SETGID:
res = snprintf(buf, MAX_LEN + 1, "pid=%d\nuid=%d\ngid=%d\neuid=%d\negid=%d\n",
p->pid,
uid_get_value(p->cred->uid),
uid_get_value(p->cred->gid),
uid_get_value(p->cred->euid),
uid_get_value(p->cred->egid));
break;
case DBG_GET_LOG:
#ifdef DEFEX_LOG_BUFFER_ENABLE
buff_size = PAGE_SIZE;
do {
msg_len = log_buffer_get(&buf[res], buff_size);
res += msg_len;
buff_size -= msg_len;
} while (msg_len && buff_size >= MAX_LEN);
if (!msg_len)
res += snprintf(&buf[res], buff_size, "=== EOF ===\n");
else
res += snprintf(&buf[res], buff_size, "=== %lu bytes left ===\n",
(unsigned long)(log_buf_in_index - log_buf_out_index) & DEFEX_LOG_BUF_MASK);
#else
res = snprintf(buf, MAX_LEN + 1, "Log buffer disabled...\n");
#endif /* DEFEX_LOG_BUFFER_ENABLE */
break;
}
return res;
}
__visible_for_testing struct kobj_attribute debug_attribute = __ATTR(debug, 0660, debug_show, debug_store);
int defex_create_debug(struct kset *defex_kset)
{
int retval;
#ifdef DEFEX_LOG_BUFFER_ENABLE
retval = log_buf_init();
if (retval)
defex_log_err("Log buffer init failed. Log output only via /dev/kmsg");
else
defex_log_info("Logging started...");
#endif /* DEFEX_LOG_BUFFER_ENABLE */
last_cmd = DBG_GET_LOG;
retval = sysfs_create_file(&defex_kset->kobj, &debug_attribute.attr);
if (retval)
return DEFEX_NOK;
kobject_uevent(&defex_kset->kobj, KOBJ_ADD);
return DEFEX_OK;
}

View file

@ -1,174 +0,0 @@
#include <linux/kobject.h>
#include "include/defex_debug.h"
#include "include/defex_rules.h"
const char header_name[16] = {"DEFEX_RULES_FILE"};
struct rule_item_struct *defex_packed_rules;
static char work_path[512];
static int packfiles_size, global_data_size;
const struct feature_match_entry feature_match[] = {
{"feature_safeplace_path", feature_safeplace_path},
{"feature_ped_exception", feature_ped_exception},
{"feature_immutable_path_open", feature_immutable_path_open},
{"feature_immutable_path_write", feature_immutable_path_write},
{"feature_immutable_src_exception", feature_immutable_src_exception},
{"feature_umhbin_path", feature_umhbin_path},
{"feature_integrity_check", feature_integrity_check},
};
const int feature_match_size = ARRAY_SIZE(feature_match);
static void feature_to_str(char *str, unsigned short flags)
{
int i;
str[0] = 0;
for (i = 0; i < feature_match_size; i++)
if (flags & feature_match[i].feature_num) {
if (str[0])
strcat(str, ", ");
strcat(str, feature_match[i].feature_name);
}
if (flags & feature_for_recovery) {
if (str[0])
strcat(str, ", ");
strcat(str, "feature_for_recovery");
}
}
static int check_array_size(struct rule_item_struct *ptr)
{
unsigned long offset = (unsigned long)ptr - (unsigned long)defex_packed_rules;
int min_size = (global_data_size < packfiles_size)?global_data_size:packfiles_size;
offset += sizeof(struct rule_item_struct);
if (offset > min_size)
return 1;
offset += ptr->size;
if (offset > min_size)
return 2;
return 0;
}
static int parse_items(struct rule_item_struct *base, int path_length, int level)
{
int l, err, ret = 0;
unsigned int offset;
struct rule_item_struct *child_item;
static char feature_list[128];
if (level > 8) {
defex_log_timeoff("Level is too deep");
return -1;
}
if (path_length > (sizeof(work_path) - 128)) {
defex_log_timeoff("Work path is too long");
return -1;
}
while (base) {
err = check_array_size(base);
if (err) {
defex_log_timeoff("%s/<?> - out of array bounds", work_path);
return -1;
}
l = base->size;
if (!l) {
defex_log_timeoff("WARNING: Name field is incorrect, structure error!");
return -1;
}
memcpy(work_path + path_length, base->name, l);
l += path_length;
work_path[l] = 0;
offset = base->next_level;
if (offset) {
if (base->feature_type & feature_is_file) {
defex_log_timeoff("%s - is a file, but has children, structure error!", work_path);
ret = -1;
} else if (base->feature_type != 0) {
feature_to_str(feature_list, base->feature_type);
defex_log_blob("%s%c - %s", work_path,
((base->feature_type & feature_is_file)?' ':'/'), feature_list);
}
child_item = GET_ITEM_PTR(offset, defex_packed_rules);
work_path[l++] = '/';
work_path[l] = 0;
err = check_array_size(child_item);
if (!err) {
err = parse_items(child_item, l, level + 1);
if (err != 0)
return err;
} else {
defex_log_timeoff("%s/<?> - out of array bounds", work_path);
ret = -1;
}
} else {
feature_to_str(feature_list, base->feature_type);
defex_log_blob("%s%c - %s", work_path,
((base->feature_type & feature_is_file)?' ':'/'), feature_list);
}
work_path[path_length] = 0;
offset = base->next_file;
base = (offset)?GET_ITEM_PTR(offset, defex_packed_rules):NULL;
}
return ret;
}
int defex_show_structure(void *packed_rules, int rules_size)
{
struct rule_item_struct *base;
int res, offset;
int first_item_size = sizeof(struct rule_item_struct) + sizeof(header_name);
defex_packed_rules = (struct rule_item_struct *)packed_rules;
work_path[0] = '/';
work_path[1] = 0;
packfiles_size = rules_size;
global_data_size = defex_packed_rules->data_size;
defex_log_timeoff("Rules binary size: %d", packfiles_size);
defex_log_timeoff("Rules internal data size: %d", global_data_size);
if (global_data_size > packfiles_size)
defex_log_timeoff("WARNING: Internal size is bigger than binary size, possible structure error!");
if (packfiles_size < first_item_size) {
defex_log_timeoff("ERROR: Too short binary size, can't continue!");
return -1;
}
if (global_data_size < first_item_size)
defex_log_timeoff("WARNING: Too short data size, possible structure error!");
if (defex_packed_rules->size != sizeof(header_name))
defex_log_timeoff("WARNING: incorrect size field (%d), possible structure error!",
(int)defex_packed_rules->size);
if (memcmp(header_name, defex_packed_rules->name, sizeof(header_name)) != 0)
defex_log_timeoff("WARNING: incorrect name field, possible structure error!");
defex_log_timeoff("File List:\n");
offset = defex_packed_rules->next_level;
base = (offset)?GET_ITEM_PTR(offset, defex_packed_rules):NULL;
if (!base) {
defex_log_timeoff("- empty list\n");
return 0;
} else if (check_array_size(base)) {
defex_log_timeoff("- list is out of array bounds!");
return -1;
}
res = parse_items(base, 1, 1);
defex_log_timeoff("== End of File List ==");
return res;
}

View file

@ -1,250 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#if 0
/* Rules start after this line */
{feature_ped_exception,"/system/bin/run-as"}, /* DEFAULT */
{feature_ped_exception,"/system/bin/dumpstate"}, /* DEFAULT */
{feature_safeplace_path,"/init"},
{feature_safeplace_path,"/system/bin/init"},
{feature_safeplace_path,"/system/bin/app_process32"},
{feature_safeplace_path,"/system/bin/app_process64"},
{feature_safeplace_path,"/system/bin/blkid"},
{feature_safeplace_path,"/system/bin/clatd"},
{feature_safeplace_path,"/system/bin/cmd"},
{feature_safeplace_path,"/system/bin/corehelper.sh"},
{feature_safeplace_path,"/system/bin/crash_dump32"},
{feature_safeplace_path,"/system/bin/crash_dump64"},
{feature_safeplace_path,"/system/bin/debuggerd"},
{feature_safeplace_path,"/system/bin/dnsmasq"},
{feature_safeplace_path,"/system/bin/dsms"},
{feature_safeplace_path,"/system/bin/dumpstate"},
{feature_safeplace_path,"/system/bin/fsck.vfat"},
{feature_safeplace_path,"/system/bin/fsck.exfat"},
{feature_safeplace_path,"/system/bin/gatekeeperd"},
{feature_safeplace_path,"/system/bin/healthd"},
{feature_safeplace_path,"/system/bin/installd"},
{feature_safeplace_path,"/system/bin/iod"},
{feature_safeplace_path,"/system/bin/ip"},
{feature_safeplace_path,"/system/bin/iptables"},
{feature_safeplace_path,"/system/bin/iptables-restore"},
{feature_safeplace_path,"/system/bin/ip6tables"},
{feature_safeplace_path,"/system/bin/ip6tables-restore"},
{feature_safeplace_path,"/system/bin/lmkd"},
{feature_safeplace_path,"/system/bin/lshal"},
{feature_safeplace_path,"/system/bin/mdf_fota"},
{feature_safeplace_path,"/system/bin/mkfs.vfat"},
{feature_safeplace_path,"/system/bin/mkfs.exfat"},
{feature_safeplace_path,"/system/bin/netd"},
{feature_safeplace_path,"/system/bin/nst"},
{feature_safeplace_path,"/system/bin/perfmond"},
{feature_safeplace_path,"/system/bin/perfprofd"},
{feature_safeplace_path,"/system/bin/sgdisk"},
{feature_safeplace_path,"/system/bin/sh"},
{feature_safeplace_path,"/system/bin/ss"},
{feature_safeplace_path,"/system/bin/storaged"},
{feature_safeplace_path,"/system/bin/tc"},
{feature_safeplace_path,"/system/bin/uncrypt"},
{feature_safeplace_path,"/system/bin/vold"},
{feature_safeplace_path,"/system/bin/webview_zygote32"},
{feature_safeplace_path,"/system/bin/grep"},
{feature_safeplace_path,"/system/bin/e2fsck"},
{feature_safeplace_path,"/system/bin/scs"},
{feature_safeplace_path,"/system/bin/vdc"},
{feature_safeplace_path,"/system/bin/vaultkeeperd"},
{feature_safeplace_path,"/system/bin/prepare_param.sh"},
{feature_safeplace_path,"/system/bin/smdexe"},
{feature_safeplace_path,"/system/bin/diagexe"},
{feature_safeplace_path,"/system/bin/ddexe"},
{feature_safeplace_path,"/system/bin/connfwexe"},
{feature_safeplace_path,"/system/bin/at_distributor"},
{feature_safeplace_path,"/system/bin/sdcard"},
{feature_safeplace_path,"/system/bin/resetreason"},
{feature_safeplace_path,"/system/bin/lpm"},
{feature_safeplace_path,"/system/bin/resize2fs"},
{feature_safeplace_path,"/system/bin/tune2fs"},
{feature_safeplace_path,"/system/bin/patchoat"},
{feature_safeplace_path,"/system/bin/knox_changer"},
{feature_safeplace_path,"/system/bin/knox_changer_recovery"},
{feature_safeplace_path,"/sbin/sswap"},
{feature_safeplace_path,"/sbin/cbd"},
{feature_safeplace_path,"/sbin/adbd"},
{feature_safeplace_path,"/sbin/recovery"},
{feature_safeplace_path,"/sbin/mke2fs_static"},
{feature_safeplace_path,"/vendor/bin/hw/wpa_supplicant"},
{feature_safeplace_path,"/vendor/bin/hw/macloader"},
{feature_safeplace_path,"/vendor/bin/hw/mfgloader"},
{feature_safeplace_path,"/sbin/dm_verity_hash"},
{feature_safeplace_path,"/sbin/dm_verity_signature_checker"},
{feature_safeplace_path,"/vendor/bin/qseecomd"},
{feature_safeplace_path,"/system/bin/vold_prepare_subdirs"},
{feature_safeplace_path,"/vendor/bin/init.qcom.early_boot.sh"},
{feature_safeplace_path,"/vendor/bin/toybox_vendor"},
{feature_safeplace_path,"/vendor/bin/toolbox"},
{feature_safeplace_path,"/vendor/bin/hw/android.hardware.usb@1.1-service.wahoo"},
{feature_safeplace_path,"/vendor/bin/hw/vendor.qti.hardware.iop@2.0-service"},
{feature_safeplace_path,"/vendor/bin/hw/vendor.qti.hardware.perf@1.0-service"},
{feature_safeplace_path,"/vendor/bin/init.qcom.class_core.sh"},
{feature_safeplace_path,"/vendor/bin/irsc_util"},
{feature_safeplace_path,"/vendor/bin/rmt_storage"},
{feature_safeplace_path,"/system/bin/toybox"},
{feature_safeplace_path,"/vendor/bin/init.qcom.usb.sh"},
{feature_safeplace_path,"/vendor/bin/tftp_server"},
{feature_safeplace_path,"/vendor/bin/init.qcom.sensors.sh"},
{feature_safeplace_path,"/system/bin/insthk"},
{feature_safeplace_path,"/vendor/bin/init.class_main.sh"},
{feature_safeplace_path,"/vendor/bin/time_daemon"},
{feature_safeplace_path,"/vendor/bin/thermal-engine"},
{feature_safeplace_path,"/system/bin/sec_diag_uart_log"},
{feature_safeplace_path,"/vendor/bin/init.qcom.sh"},
{feature_safeplace_path,"/system/bin/usbd"},
{feature_safeplace_path,"/vendor/bin/init.qcom.post_boot.sh"},
{feature_safeplace_path,"/system/bin/adbd"},
{feature_safeplace_path,"/system/bin/atrace"},
{feature_safeplace_path,"/system/bin/fsdbg"},
{feature_safeplace_path,"/system/bin/dumpsys"},
{feature_safeplace_path,"/system/bin/logcat"},
{feature_safeplace_path,"/system/bin/toolbox"},
{feature_safeplace_path,"/system/bin/mke2fs"},
{feature_safeplace_path,"/vendor/bin/cbd"},
{feature_safeplace_path,"/vendor/bin/adsprpcd"},
{feature_safeplace_path,"/sbin/e2fsdroid_static"},
{feature_safeplace_path,"/system/bin/e2fsdroid"},
{feature_safeplace_path,"/system/bin/fsck.f2fs"},
{feature_safeplace_path,"/system/bin/make_f2fs"},
{feature_safeplace_path,"/system/bin/sload_f2fs"},
{feature_safeplace_path,"/system/bin/bpfloader"},
{feature_safeplace_path,"/system/bin/wait_for_keymaster"},
{feature_safeplace_path,"/system/bin/secdiscard"},
{feature_safeplace_path,"/system/bin/idledefrag"},
{feature_safeplace_path,"/vendor/bin/init.mdm.sh"},
{feature_safeplace_path,"/vendor/bin/mdm_helper"},
{feature_safeplace_path,"/vendor/bin/ks"},
{feature_safeplace_path,"/vendor/bin/sh"},
{feature_safeplace_path,"/system/bin/e4defrag"},
{feature_safeplace_path,"/sbin/dm_verity_tz_cmd"},
{feature_safeplace_path,"/sbin/mcDriverDaemon_static"},
{feature_safeplace_path,"/sbin/qseecomfsd"},
{feature_safeplace_path,"/sbin/tzdaemon_recovery"},
{feature_safeplace_path,"/vendor/bin/hvdcp_opti"},
{feature_safeplace_path,"/sbin/mkfs.f2fs"},
{feature_safeplace_path,"/sbin/sload.f2fs"},
{feature_safeplace_path,"/system/bin/secilc"},
{feature_safeplace_path,"/system/bin/apexd"},
{feature_safeplace_path,"/system/bin/art_apex_boot_integrity"},
{feature_safeplace_path,"/system/bin/gsid"},
{feature_safeplace_path,"/system/bin/idmap2"},
{feature_safeplace_path,"/system/bin/charger"},
{feature_safeplace_path,"/system/bin/recovery"},
{feature_safeplace_path,"/system/bin/watchdogd"},
{feature_safeplace_path,"/vendor/bin/hw/vendor.qti.hardware.perf@2.0-service"},
{feature_safeplace_path,"/system/bin/netutils-wrapper-1.0"},
{feature_safeplace_path,"/system/bin/bugreport"},
{feature_safeplace_path,"/system/bin/minadbd"},
{feature_safeplace_path,"/system/bin/migrate_legacy_obb_data.sh"},
{feature_safeplace_path,"/vendor/bin/shsusrd"},
{feature_safeplace_path,"/system/bin/defrag_f2fs"},
{feature_safeplace_path,"/system/bin/fastbootd"},
{feature_safeplace_path,"/vendor/bin/hw/vendor.qti.hardware.perf@2.1-service"},
{feature_safeplace_path,"/vendor/bin/hw/vendor.qti.hardware.perf@2.2-service"},
{feature_safeplace_path,"/vendor/bin/grep"},
{feature_safeplace_path,"/vendor/bin/memlogd"},
{feature_safeplace_path,"/vendor/bin/init.insmod.sh"},
{feature_safeplace_path,"/vendor/bin/dsmsca"},
{feature_safeplace_path,"/vendor/bin/hw/android.hardware.usb@1.3-service.coral"},
{feature_safeplace_path,"/vendor/bin/hw/vendor.qti.hardware.perf-hal-service"},
{feature_safeplace_path,"/vendor/bin/iod"},
{feature_safeplace_path,"/vendor/bin/hqread"},
{feature_safeplace_path,"/system/bin/hqcpsnbin"},
{feature_safeplace_path,"/system/bin/rdxd"},
{feature_safeplace_path,"/system/system_ext/bin/dpmd"},
{feature_safeplace_path,"/vendor/bin/init.qti.dcvs.sh"},
{feature_safeplace_path,"/vendor/bin/vendor_modprobe.sh"},
{feature_safeplace_path,"/vendor/bin/init.qti.qcv.sh"},
{feature_safeplace_path,"/vendor/bin/init.qcom.crashdata.sh"},
{feature_safeplace_path,"/vendor/bin/energy-awareness"},
{feature_safeplace_path,"/vendor/bin/qcom-system-daemon"},
{feature_safeplace_path,"/vendor/bin/init.qti.kernel.sh"},
{feature_safeplace_path,"/vendor/bin/init.kernel.post_boot.sh"},
{feature_safeplace_path,"/vendor/bin/init.kernel.post_boot-lahaina.sh"},
{feature_safeplace_path,"/vendor/bin/init.qti.keymaster.sh"},
{feature_safeplace_path,"/vendor/bin/init.qti.early_init.sh"},
{feature_safeplace_path,"/vendor/bin/msm_irqbalance"},
{feature_safeplace_path,"/vendor/bin/poweropt-service"},
{feature_safeplace_path,"/vendor/bin/hw/vendor.qti.hardware.limits@1.0-service"},
{feature_safeplace_path,"/vendor/bin/thermal_manager"},
{feature_safeplace_path,"/system/bin/linkerconfig"},
{feature_safeplace_path,"/system/bin/snapshotctl"},
{feature_safeplace_path,"/system/bin/boringssl_self_test32"},
{feature_safeplace_path,"/system/bin/boringssl_self_test64"},
{feature_safeplace_path,"/vendor/bin/boringssl_self_test32"},
{feature_safeplace_path,"/vendor/bin/boringssl_self_test64"},
{feature_safeplace_path,"/apex/com.android.adbd/bin/adbd"},
{feature_safeplace_path,"/apex/com.android.sdkext/bin/derive_sdk"},
{feature_safeplace_path,"/apex/com.android.conscrypt/bin/boringssl_self_test32"},
{feature_safeplace_path,"/apex/com.android.conscrypt/bin/boringssl_self_test64"},
{feature_safeplace_path,"/system/bin/applypatch"},
{feature_safeplace_path,"/vendor/bin/applypatch"},
{feature_safeplace_path,"/system/bin/clean_scratch_files"},
{feature_safeplace_path,"/system/bin/fsverity"},
{feature_safeplace_path,"/system/bin/fsverity_init"},
{feature_safeplace_path,"/system/xbin/librank"},
{feature_safeplace_path,"/system/xbin/procrank"},
{feature_safeplace_path,"/system/xbin/showmap"},
{feature_safeplace_path,"/system/bin/librank"},
{feature_safeplace_path,"/system/bin/procrank"},
{feature_safeplace_path,"/system/bin/showmap"},
{feature_safeplace_path,"/product/bin/dmabuf_dump"},
{feature_safeplace_path,"/apex/com.android.runtime/bin/spqr"},
{feature_safeplace_path,"/system/bin/perfetto"},
{feature_safeplace_path,"/system/bin/update_verifier"},
{feature_safeplace_path,"/system/bin/bootstrap/linkerconfig"},
{feature_safeplace_path,"/apex/com.android.runtime/bin/linkerconfig"},
{feature_safeplace_path,"/system/bin/otapreopt_slot"},
{feature_safeplace_path,"/apex/com.android.art/bin/dex2oat32"},
{feature_safeplace_path,"/apex/com.android.art/bin/dex2oat64"},
{feature_safeplace_path,"/system/bin/incident"},
{feature_safeplace_path,"/tmp/update_binary;updater_intermediates/updater;obj/EXECUTABLES"},
{feature_safeplace_path,"/tmp/update-binary"},
{feature_safeplace_path,"/system/bin/install-recovery.sh"}, /* DEFAULT */
{feature_safeplace_path,"/vendor/bin/install-recovery.sh"}, /* DEFAULT */
{feature_safeplace_path,"/system/bin/odsign"}, /* DEFAULT */
{feature_safeplace_path,"/apex/com.android.art/bin/odrefresh"}, /* DEFAULT */
{feature_immutable_path_write,"/system/"}, /* DEFAULT */
{feature_immutable_path_write,"/vendor/"}, /* DEFAULT */
{feature_immutable_path_open,"/system/bin/"}, /* DEFAULT */
{feature_immutable_path_open,"/vendor/bin/"}, /* DEFAULT */
{feature_immutable_src_exception,"/system/bin/icd"},
{feature_immutable_src_exception,"/system/bin/iof"},
{feature_immutable_src_exception,"/system/bin/sh"},
{feature_immutable_src_exception,"/system/bin/app_process32"},
{feature_immutable_src_exception,"/system/bin/app_process64"},
{feature_immutable_src_exception,"/system/bin/crash_dump32"},
{feature_immutable_src_exception,"/system/bin/crash_dump64"},
{feature_immutable_src_exception,"/system/bin/mediaextractor"},
{feature_immutable_src_exception,"/system/bin/surfaceflinger"},
{feature_immutable_src_exception,"/vendor/bin/sh"},
{feature_immutable_src_exception,"/vendor/bin/hw/android.hardware.media.omx@1.0-service"},
{feature_immutable_src_exception,"/vendor/bin/snap_utility_32"},
{feature_immutable_src_exception,"/vendor/bin/snap_utility_64"},
{feature_immutable_src_exception,"/vendor/bin/icd_vendor"},
{feature_immutable_src_exception,"/vendor/bin/iof_vendor"},
{feature_immutable_src_exception,"/init"},
{feature_immutable_src_exception,"/system/bin/init"},
{feature_immutable_src_exception,"/system/bin/lshal"},
{feature_integrity_check,"/vendor/bin/hw/android.hardware.gatekeeper@1.0-service"},
{feature_integrity_check,"/vendor/bin/hw/android.hardware.keymaster@4.0-service"},
{feature_integrity_check,"/vendor/bin/hw/android.hardware.security.keymint-service"},
{feature_integrity_check,"/vendor/bin/hw/vendor.samsung.hardware.tlc.kg@1.0-service"},
{feature_integrity_check,"/vendor/bin/vendor.samsung.hardware.security.wsm@1.0-service"},
{feature_integrity_check,"/vendor/bin/vaultkeeperd"},
{feature_integrity_check,"/vendor/bin/hw/vendor.samsung.hardware.tlc.kg@1.1-service"},
/* Rules will be added here */
/* Never modify the above line. Rules will be added for buildtime */
#endif /* if 0 */

View file

@ -1,36 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include "include/defex_internal.h"
#ifdef DEFEX_PERMISSIVE_IM
unsigned char global_immutable_status = 2;
#else
unsigned char global_immutable_status = 1;
#endif /* DEFEX_PERMISSIVE_IM */
int immutable_status_store(const char *status_str)
{
int ret;
unsigned int status;
if (!status_str)
return -EINVAL;
ret = kstrtouint(status_str, 10, &status);
if (ret != 0 || status > 2)
return -EINVAL;
global_immutable_status = status;
return 0;
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include "include/defex_internal.h"
#ifdef DEFEX_PERMISSIVE_PED
unsigned char global_privesc_status = 2;
#else
unsigned char global_privesc_status = 1;
#endif /* DEFEX_PERMISSIVE_PED */
int privesc_status_store(const char *status_str)
{
int ret;
unsigned int status;
if (!status_str)
return -EINVAL;
ret = kstrtouint(status_str, 10, &status);
if (ret != 0 || status > 2)
return -EINVAL;
global_privesc_status = status;
return 0;
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include "include/defex_internal.h"
#ifdef DEFEX_PERMISSIVE_INT
unsigned char global_integrity_status = 2;
#else
unsigned char global_integrity_status = 1;
#endif /* DEFEX_PERMISSIVE_INT */
int integrity_status_store(const char *status_str)
{
int ret;
unsigned int status;
if (!status_str)
return -EINVAL;
ret = kstrtouint(status_str, 10, &status);
if (ret != 0 || status > 2)
return -EINVAL;
global_integrity_status = status;
return 0;
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include "include/defex_internal.h"
#ifdef DEFEX_PERMISSIVE_SP
unsigned char global_safeplace_status = 2;
#else
unsigned char global_safeplace_status = 1;
#endif /* DEFEX_PERMISSIVE_SP */
int safeplace_status_store(const char *status_str)
{
int ret;
unsigned int status;
if (!status_str)
return -EINVAL;
ret = kstrtouint(status_str, 10, &status);
if (ret != 0 || status > 2)
return -EINVAL;
global_safeplace_status = status;
return 0;
}

View file

@ -1,16 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include "include/defex_internal.h"
unsigned char global_trusted_map_status = DEFEX_TM_ENFORCING_MODE
#ifdef DEFEX_PERMISSIVE_TM
| DEFEX_TM_PERMISSIVE_MODE
#endif
| DEFEX_TM_DEBUG_VIOLATIONS
;

View file

@ -1,206 +0,0 @@
/*
* Copyright (c) 2020-2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/compat.h>
#include <linux/compiler.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/task.h>
#endif
#include "include/dtm.h"
#include "include/dtm_engine.h"
#include "include/dtm_log.h"
#include "include/dtm_utils.h"
/* From fs/exec.c: SM8150_Q, SM8250_Q, SM8250_R, exynos9820, exynos9830 */
struct user_arg_ptr {
#ifdef CONFIG_COMPAT
bool is_compat;
#endif
union {
const char __user *const __user *native;
#ifdef CONFIG_COMPAT
const compat_uptr_t __user *compat;
#endif
} ptr;
};
/* From fs/exec.c: SM8150_Q, SM8250_Q, SM8250_R, exynos9820, exynos9830 */
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
{
const char __user *native;
#ifdef CONFIG_COMPAT
if (unlikely(argv.is_compat)) {
compat_uptr_t compat;
if (get_user(compat, argv.ptr.compat + nr))
return ERR_PTR(-EFAULT);
return compat_ptr(compat);
}
#endif
if (get_user(native, argv.ptr.native + nr))
return ERR_PTR(-EFAULT);
return native;
}
static void dtm_kfree_args(struct dtm_context *context)
{
const char **argv;
int arg, to_free;
if (unlikely(!is_dtm_context_valid(context)))
return;
argv = context->callee_argv;
arg = min_t(int, context->callee_argc, DTM_MAX_ARGC);
to_free = context->callee_copied_argc;
context->callee_copied_argc = 0;
while (--arg >= 0 && to_free > 0) {
if (!argv[arg])
continue;
kfree(argv[arg]);
to_free--;
}
}
/*
* Gets call argument value, copying from user if needed.
*/
const char *dtm_get_callee_arg(struct dtm_context *context, int arg_index)
{
struct user_arg_ptr argv;
const char __user *user_str;
char *copy;
int max_argc, arg_len, copy_len;
if (unlikely(!is_dtm_context_valid(context)))
return NULL;
max_argc = min_t(int, context->callee_argc, DTM_MAX_ARGC);
if (unlikely((arg_index < 0) || (arg_index >= max_argc)))
return NULL;
if (context->callee_argv[arg_index])
return context->callee_argv[arg_index];
argv = *(struct user_arg_ptr *)context->callee_argv_ref;
user_str = get_user_arg_ptr(argv, arg_index);
if (IS_ERR(user_str))
return NULL;
arg_len = strnlen_user(user_str, MAX_ARG_STRLEN);
if (unlikely(!arg_len))
return NULL;
copy_len = min_t(int, arg_len, DTM_MAX_ARG_STRLEN);
copy = kzalloc(copy_len, GFP_KERNEL);
if (unlikely(!copy))
return NULL;
if (unlikely(copy_from_user(copy, user_str, copy_len)))
goto out_free_copy;
copy[copy_len - 1] = '\0';
context->callee_argv[arg_index] = copy;
context->callee_copied_argc++;
context->callee_copied_args_len += copy_len;
context->callee_total_args_len += arg_len;
return copy;
out_free_copy:
kfree(copy);
return NULL;
}
/*
* Initializes dtm context data structure.
*/
__visible_for_testing bool dtm_context_get(struct dtm_context *context,
struct defex_context *defex_context,
int callee_argc,
void *callee_argv_ref)
{
memset(context, 0, sizeof(*context));
context->defex_context = defex_context;
context->callee_argc = callee_argc;
context->callee_argv_ref = callee_argv_ref;
return true;
}
/*
* Releases resources associated to dtm context.
*/
__visible_for_testing void dtm_context_put(struct dtm_context *context)
{
dtm_kfree_args(context);
}
/*
* Gets program name for current call.
*/
const char *dtm_get_program_name(struct dtm_context *context)
{
if (unlikely(!is_dtm_context_valid(context)))
return NULL;
if (context->program_name)
return context->program_name;
context->program_name = dtm_get_callee_arg(context, 0);
if (context->program_name == NULL)
context->program_name = DTM_UNKNOWN;
return context->program_name;
}
/**
* Gets stdin mode bit for current call.
*/
int dtm_get_stdin_mode_bit(struct dtm_context *context)
{
if (unlikely(!context))
return DTM_FD_MODE_ERROR;
if (!context->stdin_mode_bit)
context->stdin_mode_bit = dtm_get_fd_mode_bit(0);
return context->stdin_mode_bit;
}
/**
* Gets stdin mode for current call.
*/
const char *dtm_get_stdin_mode(struct dtm_context *context)
{
if (unlikely(!context))
return NULL;
if (!context->stdin_mode)
context->stdin_mode = dtm_get_fd_mode_bit_name(
dtm_get_stdin_mode_bit(context));
return context->stdin_mode;
}
int defex_trusted_map_lookup(struct defex_context *defex_context,
int callee_argc, void *callee_argv_ref)
{
int ret = DTM_DENY;
struct dtm_context context;
if (unlikely(!defex_context || !(defex_context->task)))
goto out;
if (unlikely(!dtm_context_get(&context, defex_context, callee_argc, callee_argv_ref)))
goto out;
ret = dtm_enforce(&context);
dtm_context_put(&context);
out:
return ret;
}

View file

@ -1,195 +0,0 @@
/*
* Copyright (c) 2020-2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/compiler.h>
#include <linux/string.h>
#include <linux/types.h>
#include "include/defex_debug.h"
#include "include/defex_rules.h"
#include "include/dtm.h"
#include "include/dtm_engine.h"
#include "include/dtm_log.h"
#include "include/dtm_utils.h"
#include "include/ptree.h"
#define DTM_ANY_VALUE "*" /* wildcard value for files and arguments */
#if defined(DEFEX_TM_DEFAULT_POLICY_ENABLE) || defined(DEFEX_KERNEL_ONLY)
/* Kernel-only builds don't use DEFEX's dynamic policy loading mechanism. */
#define USE_EMBEDDED_POLICY
static struct PPTree embedded_header;
/* File with hardcoded policy */
#include "dtm_engine_defaultpolicy.h"
#endif
#ifdef DEFEX_KUNIT_ENABLED
static struct PPTree override_header, *pptree_override;
void dtm_engine_override_data(const unsigned char *p)
{
if (p) {
defex_log_warn("Dtm_engine data overridden");
pptree_set_data(pptree_override = &override_header, p);
} else {
defex_log_warn("Dtm_engine override data back to normal");
pptree_override = 0;
}
}
#endif
static int dtm_check_stdin(struct dtm_context *context, int allowed_stdin_modes)
{
if (unlikely(!(dtm_get_stdin_mode_bit(context) & allowed_stdin_modes))) {
dtm_report_violation(DTM_STDIN_VIOLATION, context);
return DTM_DENY;
}
return DTM_ALLOW;
}
/*
* Enforces DTM policy for an exec system call.
*/
int dtm_enforce(struct dtm_context *context)
{
const char *callee_path, *caller_path;
const char *program_name;
int ret, argc, call_argc;
const char *argument_value;
struct PPTree *pptree; /* effective header of policy rules */
struct PPTreeContext pp_ctx; /* context for policy search */
static char first_run = 1; /* flag for one-time policy actions */
if (first_run) {
if (dtm_tree.data)
defex_log_info("DTM engine: policy found");
else
defex_log_warn("DTM engine: dynamic policy not loaded");
first_run = 0;
#ifdef USE_EMBEDDED_POLICY
pptree_set_data(&embedded_header, dtm_engine_defaultpolicy);
#endif
}
defex_log_info("Pid : %d %d", current->tgid, current->pid);
if (!context || unlikely(!is_dtm_context_valid(context))) {
defex_log_info("(0) TMED no or invalid context");
return DTM_DENY;
}
/* Check callee */
callee_path = dtm_get_callee_path(context);
if (unlikely(!callee_path)) {
defex_log_info("(1) TMED null callee");
return DTM_DENY;
}
#ifdef DEFEX_KUNIT_ENABLED
if (pptree_override) /* test code has opportunity to use test policy instead */
pptree = pptree_override;
else
#endif
#ifdef USE_EMBEDDED_POLICY /* try dynamic policy first, use embedded if not found */
pptree = dtm_tree.data ? &dtm_tree : &embedded_header;
#else /* only dynamically loaded policy is acceptable */
pptree = &dtm_tree;
#endif
if (!pptree->data) { /* Should never happen */
defex_log_warn("(0) DTM engine: neither dynamic nor hardcoded rules loaded");
return DTM_ALLOW;
}
memset(&pp_ctx, 0, sizeof(pp_ctx));
if (!(pptree_find_path(pptree,
*callee_path == '/' ?
callee_path + 1 : callee_path, '/', &pp_ctx) &&
(pp_ctx.types & PTREE_DATA_PATH))) {
defex_log_info("(2) TME callee '%s' not found", callee_path);
return DTM_ALLOW;
}
/* Check caller */
caller_path = dtm_get_caller_path(context);
if (unlikely(!caller_path)) {
defex_log_info("(3) TMED callee '%s': null caller", callee_path);
return DTM_DENY;
}
if (!(pptree_find_path(pptree, caller_path + 1, '/', &pp_ctx) &&
(pp_ctx.types & PTREE_DATA_PATH))) {
defex_log_info("(4) TMED callee '%s': caller '%s' not found",
callee_path, caller_path);
dtm_report_violation(DTM_CALLER_VIOLATION, context);
return DTM_DENY;
}
/* Check program, if any */
program_name = dtm_get_program_name(context);
if (!program_name) {
defex_log_info("(5) TMED callee '%s', caller '%s': null program",
callee_path, caller_path);
return DTM_DENY;
}
pp_ctx.types |= PTREE_FIND_PEEK;
if (pptree_child_count(pptree, &pp_ctx) == 1 &&
pptree_find_path(pptree, DTM_ANY_VALUE, 0, &pp_ctx)) {
defex_log_info("[TME callee '%s', caller '%s': program may be '*...'",
callee_path, caller_path);
pp_ctx.types |= PTREE_FIND_PEEKED;
pptree_find_path(pptree, 0, 0, &pp_ctx);
} else {
pp_ctx.types &= ~PTREE_FIND_PEEK;
if (!(pptree_find_path(pptree, program_name, 0, &pp_ctx) &&
(pp_ctx.types & PTREE_DATA_INT2))) {
defex_log_info("(6) TMED callee '%s', caller '%s': program '%s' not found",
callee_path, caller_path, program_name);
dtm_report_violation(DTM_PROGRAM_VIOLATION, context);
return DTM_DENY;
}
}
/* Check standard input mode */
if (pp_ctx.types & PTREE_DATA_INT2) {
ret = dtm_check_stdin(context, pp_ctx.value.int2);
if (unlikely(ret != DTM_ALLOW)) {
defex_log_info("(7) TMED callee '%s', caller '%s', program '%s': stdin mode %d, should be %d",
callee_path, caller_path,
program_name ? program_name : "(null)",
dtm_get_stdin_mode_bit(context), pp_ctx.value.int2);
return ret;
}
}
/* Check program arguments, if any */
pp_ctx.types |= PTREE_FIND_CONTINUE;
for (call_argc = context->callee_argc, argc = 1;
argc <= call_argc && pptree_child_count(pptree, &pp_ctx);
++argc) {
pp_ctx.types |= PTREE_FIND_PEEK;
if (pptree_find_path(pptree, DTM_ANY_VALUE, 0, &pp_ctx)) {
defex_log_info("(8) TME callee '%s', caller '%s', program '%s': any arguments accepted",
callee_path, caller_path, program_name);
return DTM_ALLOW;
}
pp_ctx.types &= PTREE_FIND_PEEKED;
pp_ctx.types |= PTREE_FIND_CONTINUE;
argument_value = dtm_get_callee_arg(context, argc);
if (!pptree_find_path(pptree, argument_value, 0, &pp_ctx)) {
defex_log_info(
"(9) TMED callee '%s', caller '%s', program '%s': argument '%s' (%d of %d) not found",
callee_path, caller_path, program_name,
argument_value ? argument_value : "(null)",
argc, call_argc);
dtm_report_violation(DTM_ARGUMENTS_VIOLATION, context);
return DTM_DENY;
}
if (pp_ctx.value.bits) {
defex_log_info("(10) TME callee '%s', caller '%s', program '%s': argument '%s' accepts '*'",
callee_path, caller_path, program_name,
argument_value ? argument_value : "(null)");
return DTM_ALLOW;
}
}
if (call_argc && argc > call_argc)
defex_log_info("TME callee '%s', caller '%s', program '%s': all %d argument(s) checked",
callee_path, caller_path, program_name, call_argc);
return DTM_ALLOW;
}

View file

@ -1,4 +0,0 @@
static unsigned char dtm_engine_defaultpolicy[] = {
80,80,84,114,101,101,45,1,0,0,0,0,1,0,0,0,0,1,0,0,0,
0,1,0,0,0,0,1,0,1,1,0,0,
};

View file

@ -1,115 +0,0 @@
/*
* Copyright (c) 2020-2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/string.h>
#include <linux/types.h>
#include "include/dtm.h"
#include "include/dtm_log.h"
#include "include/dtm_utils.h"
#define DTM_MAX_LOG_SIZE (1024)
#define DTM_MAX_DETAIL_SIZE (1024)
#ifdef DEFEX_DSMS_ENABLE
#include <linux/dsms.h>
#else
#define DSMS_SUCCESS (0)
#define dsms_send_message(feature_code, message, value) (DSMS_SUCCESS)
#endif
static inline bool should_send_dsms_event(void)
{
#ifdef DEFEX_DSMS_ENABLE
return IS_ENABLED(CONFIG_SECURITY_DSMS);
#else
return false;
#endif
}
__visible_for_testing void dtm_append_argv(char *message, size_t size,
char separator,
int argc, const char **argv)
{
const char *from;
char *to;
size_t len;
int arg, available;
if (!message || size <= 0 || !separator)
return;
if (argc <= 0 || !argv || !argv[0])
return;
arg = 0;
len = strnlen(message, size);
available = size - len - 1;
to = message + len;
while (arg < argc && available > 0) {
from = argv[arg++];
if (!from)
from = "(null)";
len = strnlen(from, available);
strncpy(to, from, len);
to += len;
available -= len;
if (available-- > 0)
*to++ = separator;
}
*to = 0;
}
__visible_for_testing void dtm_prepare_message(char *message, size_t size,
const char *where, const char *sep,
struct dtm_context *context)
{
int total_argc, max_argc, arg;
/* load all arguments to update attributes and fill arg values */
total_argc = context->callee_argc;
max_argc = min_t(int, total_argc, ARRAY_SIZE(context->callee_argv));
if (context->callee_copied_argc != max_argc)
for (arg = 0; arg < max_argc; arg++)
if (!context->callee_argv[arg])
dtm_get_callee_arg(context, arg);
snprintf(message, size, "%s%s%d:%d:%ld:%ld:%s:%s:%s:", where, sep,
context->callee_copied_argc, total_argc,
context->callee_copied_args_len, context->callee_total_args_len,
dtm_get_caller_path(context), dtm_get_callee_path(context),
dtm_get_stdin_mode(context));
dtm_append_argv(message, size, ':', max_argc, context->callee_argv);
}
#ifdef DEFEX_DEBUG_ENABLE
void dtm_debug_call(const char *where, struct dtm_context *context)
{
char message[DTM_MAX_LOG_SIZE];
dtm_prepare_message(message, sizeof(message), where, ": ", context);
DTM_LOG_DEBUG("%s", message);
}
#endif
noinline void dtm_report_violation(const char *feature_code,
struct dtm_context *context)
{
char message[DTM_MAX_DETAIL_SIZE + 1];
int ret;
dtm_prepare_message(message, sizeof(message), "", "", context);
DTM_DEBUG(VIOLATIONS, "[%s]%s", feature_code, message);
if (should_send_dsms_event()) {
ret = dsms_send_message(feature_code, message, 0);
if (unlikely(ret != DSMS_SUCCESS))
DTM_LOG_ERROR("Error %d while sending DSMS report", ret);
}
}

View file

@ -1,137 +0,0 @@
/*
* Copyright (c) 2020-2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/compiler.h>
#include <linux/cred.h>
#include <linux/dcache.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/mm.h>
#include <linux/path.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/uidgid.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/mm.h>
#endif
#include "include/dtm.h"
#include "include/dtm_log.h"
#include "include/dtm_utils.h"
const char * const DTM_UNKNOWN = "<unknown>";
static inline int dtm_get_file_attr(struct path *path, struct kstat *stat)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
return vfs_getattr(path, stat, STATX_BASIC_STATS, 0);
#else
return vfs_getattr(path, stat);
#endif
}
/*
* Gets mode bit value for a file status.
*/
int dtm_get_stat_mode_bit(struct kstat *stat)
{
int mode, mode_bit;
if (!stat)
return DTM_FD_MODE_ERROR;
mode = (stat->mode) & S_IFMT;
switch (mode) {
case S_IFBLK:
mode_bit = DTM_FD_MODE_BLK;
break;
case S_IFCHR:
mode_bit = DTM_FD_MODE_CHR;
break;
case S_IFDIR:
mode_bit = DTM_FD_MODE_DIR;
break;
case S_IFIFO:
mode_bit = DTM_FD_MODE_FIFO;
break;
case S_IFLNK:
mode_bit = DTM_FD_MODE_LNK;
break;
case S_IFREG:
mode_bit = DTM_FD_MODE_REG;
break;
case S_IFSOCK:
mode_bit = DTM_FD_MODE_SOCK;
break;
default:
mode_bit = DTM_FD_MODE_UNKNOWN;
DTM_LOG_ERROR("Unknown stat mode %d", mode);
}
return mode_bit;
}
/*
* Gets mode bit value for a file descriptor.
*/
int dtm_get_fd_mode_bit(int fd)
{
struct kstat stat;
struct fd sf;
int error;
if (fd < 0)
return DTM_FD_MODE_ERROR;
sf = fdget_raw(fd);
if (unlikely(!sf.file))
return DTM_FD_MODE_CLOSED;
error = dtm_get_file_attr(&sf.file->f_path, &stat);
fdput(sf);
if (unlikely(error < 0))
return DTM_FD_MODE_ERROR;
return dtm_get_stat_mode_bit(&stat);
}
/*
* Gets printable name for a fd mode bit value.
*/
const char *dtm_get_fd_mode_bit_name(int mode_bit)
{
switch (mode_bit) {
case DTM_FD_MODE_NONE:
return "NONE";
case DTM_FD_MODE_BLK:
return "BLK";
case DTM_FD_MODE_CHR:
return "CHR";
case DTM_FD_MODE_DIR:
return "DIR";
case DTM_FD_MODE_FIFO:
return "FIFO";
case DTM_FD_MODE_LNK:
return "LNK";
case DTM_FD_MODE_REG:
return "REG";
case DTM_FD_MODE_SOCK:
return "SOCK";
case DTM_FD_MODE_CLOSED:
return "CLOSED";
case DTM_FD_MODE_ERROR:
return "ERROR";
case DTM_FD_MODE_UNKNOWN:
return "UNKNOWN";
}
DTM_LOG_ERROR("Unexpected mode bit %d", mode_bit);
return "INVALID";
}

View file

@ -1,84 +0,0 @@
/*
* Copyright (c) 2020-2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef _INCLUDE_DTM_H
#define _INCLUDE_DTM_H
#include <linux/binfmts.h>
#include <linux/compiler.h>
#include <linux/defex.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/types.h>
#include "dtm_utils.h"
#include "include/defex_internal.h"
// DTM Kernel Interface
#define DTM_CALLER_VIOLATION "DTM1"
#define DTM_PROGRAM_VIOLATION "DTM2"
#define DTM_STDIN_VIOLATION "DTM3"
#define DTM_ARGUMENTS_VIOLATION "DTM4"
enum dtm_result_code {
DTM_ALLOW = DEFEX_ALLOW,
DTM_DENY = -DEFEX_DENY,
};
enum dtm_constants {
DTM_MAX_ARGC = 5, // max args checked (incl. program name)
DTM_MAX_ARG_STRLEN = 100, // max arg len checked (including '\0')
};
struct dtm_context {
struct defex_context *defex_context;
void *callee_argv_ref;
const char *callee_argv[DTM_MAX_ARGC];
int callee_argc;
int callee_copied_argc;
long callee_copied_args_len;
long callee_total_args_len;
const char *program_name;
const char *stdin_mode;
int stdin_mode_bit;
};
/* Verifies if a dtm_context was properly initialized */
static inline bool is_dtm_context_valid(struct dtm_context *context)
{
return !ZERO_OR_NULL_PTR(context)
&& !ZERO_OR_NULL_PTR(context->defex_context)
&& !ZERO_OR_NULL_PTR(context->defex_context->task)
&& !ZERO_OR_NULL_PTR(context->callee_argv_ref);
}
/* Gets caller path for current call */
static inline const char *dtm_get_caller_path(struct dtm_context *context)
{
return get_dc_process_name(context->defex_context);
}
/* Gets callee path for current call */
static inline const char *dtm_get_callee_path(struct dtm_context *context)
{
return get_dc_target_name(context->defex_context);
}
/* Gets program name for current call */
const char *dtm_get_program_name(struct dtm_context *context);
/* Gets stdin mode bit for current call */
int dtm_get_stdin_mode_bit(struct dtm_context *context);
/* Gets stdin mode for current call */
const char *dtm_get_stdin_mode(struct dtm_context *context);
/* Gets call argument value, copying from user if needed */
const char *dtm_get_callee_arg(struct dtm_context *context, int arg_index);
#endif /* _INCLUDE_DTM_H */

View file

@ -1,21 +0,0 @@
/*
* Copyright (c) 2020-2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef _INCLUDE_DTM_ENGINE_H
#define _INCLUDE_DTM_ENGINE_H
#include "dtm.h"
/* Enforces DTM policy for an exec system call */
extern int dtm_enforce(struct dtm_context *context);
#ifdef DEFEX_KUNIT_ENABLED
/* Replaces DTM policy (use NULL to return to normal) */
extern void dtm_engine_override_data(const unsigned char *);
#endif
#endif /* _INCLUDE_DTM_ENGINE_H */

View file

@ -1,43 +0,0 @@
/*
* Copyright (c) 2020-2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef _INCLUDE_DTM_LOG_H
#define _INCLUDE_DTM_LOG_H
#define DTM_TAG "[DTM] "
#define DTM_LOG_INFO(format, ...) pr_info(DTM_TAG format, ##__VA_ARGS__)
#define DTM_LOG_ERROR(format, ...) pr_err(DTM_TAG format, ##__VA_ARGS__)
#define DTM_LOG_DEBUG(format, ...) pr_debug(DTM_TAG format, ##__VA_ARGS__)
struct dtm_context;
extern noinline void dtm_report_violation(const char *feature_code,
struct dtm_context *context);
#ifdef DEFEX_DEBUG_ENABLE
#define DTM_DEBUG(mode, format, ...) \
do { \
if (defex_tm_mode_enabled(DEFEX_TM_DEBUG_##mode)) \
DTM_LOG_DEBUG(format, ##__VA_ARGS__); \
} while (0)
#define DTM_DEBUG_CALL(message, context) \
do { \
if (defex_tm_mode_enabled(DEFEX_TM_DEBUG_CALLS)) \
dtm_debug_call("dtm_enforce", context); \
} while (0)
extern void dtm_debug_call(const char *where, struct dtm_context *context);
#else
#define DTM_DEBUG(mode, format, ...) (0)
#define DTM_DEBUG_CALL(message, context) (0)
#endif /* DEFEX_DEBUG_ENABLE */
#endif /* _INCLUDE_DTM_LOG_H */

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2020-2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef _INCLUDE_DTM_UTILS_H
#define _INCLUDE_DTM_UTILS_H
#include <linux/cred.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/string.h>
#include "../../include/defex_internal.h"
enum dtm_stdin_descriptor_allow {
DTM_FD_MODE_NONE = 0,
DTM_FD_MODE_BLK = 1,
DTM_FD_MODE_CHR = 2,
DTM_FD_MODE_DIR = 4,
DTM_FD_MODE_FIFO = 8,
DTM_FD_MODE_LNK = 16,
DTM_FD_MODE_REG = 32,
DTM_FD_MODE_SOCK = 64,
DTM_FD_MODE_CLOSED = 128,
DTM_FD_MODE_ERROR = 256,
DTM_FD_MODE_UNKNOWN = 512,
};
extern const char * const DTM_UNKNOWN;
/* Gets mode bit value for a file status */
int dtm_get_stat_mode_bit(struct kstat *stat);
/* Gets mode bit value for a file descriptor */
extern int dtm_get_fd_mode_bit(int fd);
/* Gets printable name for a fd mode bit value */
const char *dtm_get_fd_mode_bit_name(int mode_bit);
#endif /* _INCLUDE_DTM_UTILS_H */

View file

@ -1,475 +0,0 @@
/*
* Copyright (c) 2021-2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/slab.h>
#include <linux/string.h>
#include "include/defex_debug.h"
#include "include/ptree.h"
/* Functions for "using" (i.e., loading and searching) p-tree in portable
* variant.
*/
/* Big-endian uchar -> int */
static unsigned int charp2UInt(const unsigned char *p, int size)
{
unsigned int i = *p;
if (size > 1) {
i = (i << 8) | p[1];
if (size > 2) {
i = (i << 8) | p[2];
if (size > 3)
i = (i << 8) | p[3];
}
}
return i;
}
/* Checks magic number, loads important constants from prologue
*/
static int pptree_set_header(struct PPTree *tree)
{
const unsigned char *pp = tree->data;
/* Only PPTREE_MAGIC_FIXEDSIZE bytes are mandatory, remaining
* two can encode version information for compatibility
*/
if (strncmp((char *)pp, PPTREE_MAGIC, PPTREE_MAGIC_FIXEDSIZE)) {
defex_log_warn("Ptree: Bad magic number");
return -1;
}
pp += PPTREE_MAGIC_FIXEDSIZE + 2;
tree->sTable.fullSize = charp2UInt(pp, UPPER_COUNT_SIZE);
pp += UPPER_COUNT_SIZE;
tree->sTable.size = charp2UInt(pp, UPPER_COUNT_SIZE);
pp += UPPER_COUNT_SIZE;
tree->sTable.indexSize = charp2UInt(pp++, 1);
tree->sTable.table = pp;
pp += tree->sTable.fullSize;
tree->bTable.fullSize = charp2UInt(pp, UPPER_COUNT_SIZE);
pp += UPPER_COUNT_SIZE;
tree->bTable.size = charp2UInt(pp, UPPER_COUNT_SIZE);
pp += UPPER_COUNT_SIZE;
tree->bTable.indexSize = charp2UInt(pp++, 1);
tree->bTable.table = pp;
pp += tree->bTable.fullSize;
tree->nodes.childCountSize = charp2UInt(pp++, 1);
tree->nodes.offsetSize = charp2UInt(pp++, 1);
tree->nodes.root = pp;
return 0;
}
int pptree_set_data(struct PPTree *tree, const unsigned char *data)
{
tree->data = data;
tree->allocated = 0;
return pptree_set_header(tree);
}
void pptree_free(struct PPTree *tree)
{
if (tree->allocated && tree->data) {
kfree((void *)tree->data);
tree->data = 0;
tree->allocated = 0;
}
}
/* Gets a string (either a component of a search key or data associated
* with an item) given index
*/
static const unsigned char *pptree_string(const struct PPTree *tree, int i)
{
const unsigned char *sTable = tree->sTable.table;
int index, bcs = tree->sTable.indexSize;
if (i < 0 || i >= tree->sTable.size) {
defex_log_warn("Ptree: bad string index: %d (max %d)", i, tree->sTable.size);
return 0;
}
index = charp2UInt(sTable + i * bcs, bcs);
return sTable + (1 + tree->sTable.size) * bcs + index;
}
/* Gets a bytearray given index */
static const unsigned char *pptree_bytearray(const struct PPTree *tree, int i,
int *length)
{
const unsigned char *bTable = tree->bTable.table;
int index, indexNext, bcs = tree->bTable.indexSize;
if (i < 0 || i >= tree->bTable.size) {
defex_log_warn("Ptree: Bad bytearray index: %d (max %d)", i, tree->bTable.size);
if (length)
*length = 0;
return "";
}
index = charp2UInt(bTable + i * bcs, bcs);
if (length) {
indexNext = charp2UInt(bTable + (i + 1) * bcs, bcs);
*length = indexNext - index;
}
return bTable + (1 + tree->bTable.size) * bcs + index;
}
/* Given a pointer to the start of a tree node, load important values
* and advance pointer to the start of item index.
*/
static void load_node_prologue(const struct PPTree *tree,
const unsigned char **p,
unsigned int *itemSize,
unsigned int *dataTypes,
unsigned int *childCount)
{
/* <dtTypes> is the |-ing of data masks of all items in this node,
* thus all possible data types associated to items.
* By extension, it determines <itSize>, which is the size in bytes
* of all items in this node.
*/
int dtTypes = charp2UInt((*p)++, 1),
itSize = tree->sTable.indexSize + tree->nodes.offsetSize;
if (dtTypes && itSize) {
++itSize;
if (dtTypes & PTREE_DATA_BYTES)
itSize += tree->bTable.indexSize;
if (dtTypes & PTREE_DATA_STRING)
itSize += tree->sTable.indexSize;
if (dtTypes & PTREE_DATA_INT1)
itSize++;
if (dtTypes & PTREE_DATA_INT2)
itSize += 2;
if (dtTypes & PTREE_DATA_INT4)
itSize += 4;
if (dtTypes & PTREE_DATA_PATH)
itSize += tree->nodes.offsetSize;
}
if (childCount)
*childCount = charp2UInt(*p, tree->nodes.childCountSize);
*p += tree->nodes.childCountSize;
if (dataTypes)
*dataTypes = dtTypes;
if (itemSize)
*itemSize = itSize;
}
/* Calculate offset from root node. It depends on a previous search, if any */
static int pptree_get_offset(const struct PPTree *tree,
struct PPTreeContext *ctx)
{
return ctx ?
/* Continue from the result of a previous search? */
(ctx->types & PTREE_FIND_CONTINUE) && ctx->last ?
ctx->last - tree->nodes.root :
/* Continue from subpath of a previous search, if any? */
ctx->types & PTREE_DATA_PATH ?
ctx->value.childPath :
0 :
0; /* No context, use root itself */
}
/* Load item-related data. <p> should be pointing to data type byte */
static const unsigned char *pptree_get_itemData(const struct PPTree *tree,
const unsigned char *p,
int dataTypes,
struct PPTreeContext *ctx)
{
memset(&ctx->value, 0, sizeof(ctx->value));
ctx->types = (ctx->types & ~PTREE_DATA_MASK) | charp2UInt(p++, 1);
if (dataTypes & PTREE_DATA_BYTES) {
if (ctx->types & PTREE_DATA_BYTES)
ctx->value.bytearray.bytes =
pptree_bytearray(tree,
charp2UInt(p,
tree->bTable.indexSize),
&ctx->value.bytearray.length);
p += tree->bTable.indexSize;
}
if (dataTypes & PTREE_DATA_STRING) {
if (ctx->types & PTREE_DATA_STRING)
ctx->value.string =
(const char *)
pptree_string(tree,
charp2UInt(p,
tree->sTable.indexSize));
p += tree->sTable.indexSize;
}
if (dataTypes & PTREE_DATA_BITA)
ctx->value.bits = (ctx->types & PTREE_DATA_BITA) &&
(ctx->types & PTREE_DATA_BITA_MASK)
? 1 : 0;
if (dataTypes & PTREE_DATA_INT1) {
if (ctx->types & PTREE_DATA_INT1)
ctx->value.int1 = charp2UInt(p, 1);
++p;
}
if (dataTypes & PTREE_DATA_INT2) {
if (ctx->types & PTREE_DATA_INT2)
ctx->value.int2 = charp2UInt(p, 2);
p += 2;
}
if (dataTypes & PTREE_DATA_INT4) {
if (ctx->types & PTREE_DATA_INT4)
ctx->value.int4 = charp2UInt(p, 4);
p += 4;
}
if (dataTypes & PTREE_DATA_PATH) {
if (ctx->types & PTREE_DATA_PATH)
ctx->value.childPath =
charp2UInt(p, tree->nodes.offsetSize);
p += tree->nodes.offsetSize;
}
return p;
}
int pptree_find(const struct PPTree *tree, const char **path, int pathLen,
struct PPTreeContext *ctx)
{
int depth;
unsigned int dataTypes = 0;
const unsigned char *pFound = 0,
*p = tree->nodes.root + pptree_get_offset(tree, ctx);
if (ctx->types & PTREE_FIND_PEEKED) {
/* If a previous call used PTREE_FIND_PEEK ignore <path>,
* only advance context's offset
*/
if (ctx->lastPeeked) {
ctx->last = ctx->lastPeeked;
ctx->lastPeeked = 0;
return 1;
}
ctx->types &= ~(PTREE_FIND_PEEK | PTREE_FIND_PEEKED);
}
if (pathLen < 1)
return 0;
for (depth = 0; depth < pathLen; ++depth) {
const char *s;
int rCmp, sIndex, i;
unsigned int itemSize, childCount;
load_node_prologue(tree, &p, &itemSize, &dataTypes, &childCount);
rCmp = -1;
if (childCount < 5) { /* linear ordered search */
for (i = 0; i < childCount; ++i) {
sIndex = charp2UInt(p + i * itemSize,
tree->sTable.indexSize);
rCmp = strncmp(path[depth],
(const char *)
pptree_string(tree, sIndex),
PTREE_FINDPATH_MAX);
if (!rCmp)
break;
if (rCmp < 0)
return 0;
}
if (i == childCount)
return 0;
} else { /* binary search */
int l = 0, r = childCount - 1;
while (l <= r) {
i = l + (r - l) / 2;
sIndex = charp2UInt(p + i * itemSize,
tree->sTable.indexSize);
s = (const char *)pptree_string(tree, sIndex);
rCmp = strncmp(path[depth], s,
PTREE_FINDPATH_MAX);
if (rCmp < 0)
r = i - 1;
else
if (rCmp)
l = i + 1;
else
break;
}
if (rCmp)
return 0;
}
pFound = p + i * itemSize + tree->sTable.indexSize;
p = tree->nodes.root + charp2UInt(pFound,
tree->nodes.offsetSize);
}
if (ctx) {
if (ctx->types & PTREE_FIND_PEEK)
/* Don't advance context, just store it here */
ctx->lastPeeked = p;
else {
ctx->last = p;
ctx->lastPeeked = 0;
}
if (dataTypes)
pptree_get_itemData(tree,
pFound + tree->nodes.offsetSize,
dataTypes, ctx);
else
/* Clear all bits for associated data */
ctx->types &= ~PTREE_DATA_MASK;
}
return 1;
}
int pptree_find_path(const struct PPTree *tree, const char *path, char delim,
struct PPTreeContext *ctx)
{
int i, itemCount, findRes, flags = ctx->types;
char *ppath, *p, **pathItems;
const char *q, *pathItems1[1];
if (!path)
return 0;
/* Convenience: split <path> in components, invoke pptree_find */
if (ctx->types & PTREE_FIND_PEEKED) {
/* No path array to fill, just use last result */
pathItems = 0;
itemCount = 0;
} else {
if (!delim) {
/* Special case, consider the whole string as
* a single component
*/
pathItems = (char **)pathItems1;
pathItems1[0] = path;
itemCount = 1;
} else {
ppath = kstrndup(path, PTREE_FINDPATH_MAX, GFP_KERNEL);
if (!ppath)
return 0;
for (itemCount = *path ? 1 : 0, q = path; *q; ++q)
if (*q == delim)
++itemCount;
pathItems = kmalloc((itemCount ? itemCount : 1) *
sizeof(const char *),
GFP_KERNEL);
if (!pathItems) {
kfree((void *)ppath);
return 0;
}
*pathItems = ppath;
for (i = 1, p = ppath; *p; ++p)
if (*p == delim) {
*p = 0;
if (i < itemCount)
pathItems[i++] = p + 1;
}
}
}
findRes = pptree_find(tree, (const char **)pathItems, itemCount, ctx);
if (!(flags & PTREE_FIND_PEEKED) && delim) {
kfree((void *) pathItems);
kfree((void *) ppath);
}
return findRes;
}
int pptree_child_count(const struct PPTree *tree,
struct PPTreeContext *ctx)
{
const unsigned char *p = tree->nodes.root +
pptree_get_offset(tree, ctx);
unsigned int childCount;
load_node_prologue(tree, &p, 0, 0, &childCount);
return childCount;
}
int pptree_iterate_children(const struct PPTree *tree,
struct PPTreeContext *ctx,
int (*f)(const struct PPTree *tree,
const char *name,
const struct PPTreeContext *itemData,
void *data),
void *data)
{
const unsigned char *p;
unsigned int i, childCount, itemSize, dataTypes, sIndex;
int ret;
if (!f)
return 0;
p = tree->nodes.root + pptree_get_offset(tree, ctx);
load_node_prologue(tree, &p, &itemSize, &dataTypes, &childCount);
for (ret = i = 0; i < childCount; ++i) {
struct PPTreeContext itemData;
sIndex = charp2UInt(p, tree->sTable.indexSize);
if (dataTypes)
pptree_get_itemData(tree,
p + tree->nodes.offsetSize +
tree->nodes.offsetSize,
dataTypes, &itemData);
else
itemData.types = 0;
ret = (*f)(tree, (const char *)pptree_string(tree, sIndex),
&itemData, data);
if (ret < 0)
return ret;
p += itemSize;
}
return ret;
}
/* Recursively traverses all children in subpath of a node given
* by <tree>+<offset>, invoking <f> on all paths ending on a leaf.
* Returns last result of <f>. Stops prematurely if <f> returns nonzero.
*/
static int pptree_iterate_subpaths(const struct PPTree *tree,
int offset, int pathDepth,
int (*f)(const struct PPTree *tree,
const char **path,
int pathLen, void *data),
const char **path, int maxDepth,
void *data)
{
const unsigned char *p = tree->nodes.root + offset;
unsigned int i, childCount, itemSize, dataTypes;
load_node_prologue(tree, &p, &itemSize, &dataTypes, &childCount);
for (i = 0; i < childCount; ++i) {
const unsigned char *pp = p + i * itemSize;
int sIndex, childIndex, j;
sIndex = charp2UInt(pp, tree->sTable.indexSize);
pp += tree->sTable.indexSize;
childIndex = charp2UInt(pp, tree->nodes.offsetSize);
pp += tree->nodes.offsetSize;
path[pathDepth] = (const char *)pptree_string(tree, sIndex);
if (childIndex) {
if (pathDepth < maxDepth) {
j = pptree_iterate_subpaths(tree, childIndex,
pathDepth + 1, f,
path, maxDepth,
data);
if (j)
return j;
}
} else
if (f) {
int j = (*f)(tree, path, pathDepth + 1, data);
if (j)
return j;
}
}
return 0;
}
int pptree_iterate_paths(const struct PPTree *tree,
struct PPTreeContext *ctx,
int (*f)(const struct PPTree *tree,
const char **path,
int pathLen, void *data),
const char **path, int maxPathLen,
void *data)
{
return pptree_iterate_subpaths(tree, pptree_get_offset(tree, ctx),
0, f, path, maxPathLen, data);
}

View file

@ -1,36 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef __DEFEX_CACHES_H
#define __DEFEX_CACHES_H
#include "defex_config.h"
#include "defex_internal.h"
#define FILE_CACHE_SIZE 0x40
struct defex_file_cache_entry {
int prev_entry;
int next_entry;
int pid;
struct file *file_addr;
};
struct defex_file_cache_list {
struct defex_file_cache_entry entry[FILE_CACHE_SIZE];
int first_entry;
int last_entry;
};
void defex_file_cache_init(void);
void defex_file_cache_add(int pid, struct file *file_addr);
void defex_file_cache_update(struct file *file_addr);
void defex_file_cache_delete(int pid);
struct file *defex_file_cache_find(int pid);
#endif /* __DEFEX_CACHES_H */

View file

@ -1,229 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef __CONFIG_SECURITY_DEFEX_CATCH_LIST_H
#define __CONFIG_SECURITY_DEFEX_CATCH_LIST_H
#include "defex_config.h"
//DEFINES//////////////////////////////////////////////////////////////////////
#define defex_do_str(s) #s
#define defex_make_str(s) defex_do_str(s)
#define SYSCALL_CATCH(name, errno) \
[__NR_##name] = { \
.local_syscall = __DEFEX_##name, \
.err_code = (errno) \
}
enum defex_local_syscall {
__DEFEX_empty = 0,
__DEFEX_rmdir,
__DEFEX_utimes,
__DEFEX_stat,
__DEFEX_lstat,
__DEFEX_umount,
__DEFEX_utime,
__DEFEX_futimesat,
__DEFEX_uselib,
__DEFEX_send,
__DEFEX_ustat,
__DEFEX_getdents,
__DEFEX_recv,
__DEFEX_fork,
__DEFEX_vfork,
__DEFEX_sigprocmask,
__DEFEX_sigpending,
__DEFEX_sigaction,
__DEFEX_sigaltstack,
__DEFEX_sigsuspend,
__DEFEX_truncate64,
__DEFEX_ftruncate64,
__DEFEX_fstat64,
__DEFEX_fstatat64,
__DEFEX_statfs64,
__DEFEX_stat64,
__DEFEX_lstat64,
__DEFEX_eventfd,
__DEFEX_epoll_create,
__DEFEX_shmget,
__DEFEX_shmctl,
__DEFEX_semctl,
__DEFEX_move_pages,
__DEFEX_lookup_dcookie,
__DEFEX_truncate,
__DEFEX_ftruncate,
__DEFEX_chdir,
__DEFEX_chroot,
__DEFEX_fchmod,
__DEFEX_fchmodat,
__DEFEX_fchownat,
__DEFEX_fchown,
__DEFEX_open, // -> included to the source
__DEFEX_openat, // -> included to the source
__DEFEX_write, // -> included to the source
__DEFEX_writev,
__DEFEX_pwrite64,
__DEFEX_pwritev,
__DEFEX_sendfile,
__DEFEX_signalfd4,
__DEFEX_vmsplice,
__DEFEX_splice,
__DEFEX_tee,
__DEFEX_fsync,
__DEFEX_fdatasync,
__DEFEX_sync_file_range,
__DEFEX_acct,
__DEFEX_sched_setparam,
__DEFEX_sched_setscheduler,
__DEFEX_sched_setaffinity,
__DEFEX_reboot,
__DEFEX_mq_timedsend,
__DEFEX_mq_timedreceive,
__DEFEX_msgrcv,
__DEFEX_msgsnd,
__DEFEX_semtimedop,
__DEFEX_add_key,
__DEFEX_request_key,
__DEFEX_keyctl,
__DEFEX_mmap,
__DEFEX_mincore,
__DEFEX_mbind,
__DEFEX_set_mempolicy,
__DEFEX_migrate_pages,
__DEFEX_accept4,
__DEFEX_recvmmsg,
__DEFEX_link,
__DEFEX_unlink,
__DEFEX_mknod,
__DEFEX_chmod,
__DEFEX_chown,
__DEFEX_mknodat,
__DEFEX_mkdirat,
__DEFEX_unlinkat,
__DEFEX_symlinkat,
__DEFEX_linkat,
__DEFEX_mkdir,
__DEFEX_lchown,
__DEFEX_rename,
__DEFEX_epoll_wait,
__DEFEX_sysctl,
__DEFEX_renameat,
__DEFEX_umount2,
__DEFEX_mount,
__DEFEX_pivot_root,
__DEFEX_utimensat,
__DEFEX_fcntl,
__DEFEX_kexec_load,
__DEFEX_ptrace,
__DEFEX_setgroups,
__DEFEX_settimeofday,
__DEFEX_delete_module,
__DEFEX_init_module,
__DEFEX_capset,
__DEFEX_setpriority,
__DEFEX_setregid,
__DEFEX_setgid,
__DEFEX_setreuid,
__DEFEX_setuid,
__DEFEX_setresuid,
__DEFEX_setresgid,
__DEFEX_setpgid,
__DEFEX_setfsuid, // -> included to the source
__DEFEX_setfsgid, // -> included to the source
__DEFEX_getsid,
__DEFEX_setsid,
__DEFEX_sethostname,
__DEFEX_setdomainname,
__DEFEX_setrlimit,
__DEFEX_umask, // -> included to the source
__DEFEX_prctl,
__DEFEX_getcpu,
__DEFEX_kill,
__DEFEX_tgkill,
__DEFEX_tkill,
__DEFEX_rt_tgsigqueueinfo,
__DEFEX_rt_sigqueueinfo,
__DEFEX_listen,
__DEFEX_accept,
__DEFEX_shutdown,
__DEFEX_shmat,
__DEFEX_shmdt,
__DEFEX_semget,
__DEFEX_semop,
__DEFEX_faccessat,
__DEFEX_fchdir,
__DEFEX_fstat,
__DEFEX_readlinkat,
__DEFEX_statfs,
__DEFEX_fstatfs,
__DEFEX_getcwd,
__DEFEX_futex,
__DEFEX_perf_event_open,
__DEFEX_socket,
__DEFEX_bind,
__DEFEX_connect,
__DEFEX_sendto,
__DEFEX_mprotect,
__DEFEX_mremap,
__DEFEX_pselect6,
__DEFEX_ioctl,
__DEFEX_ioprio_set,
__DEFEX_pipe2,
__DEFEX_getdents64,
__DEFEX_setitimer,
__DEFEX_capget,
__DEFEX_getresuid,
__DEFEX_getresgid,
__DEFEX_rt_sigprocmask,
__DEFEX_socketpair,
__DEFEX_getsockname,
__DEFEX_getpeername,
__DEFEX_recvfrom,
__DEFEX_setsockopt,
__DEFEX_sendmsg,
__DEFEX_recvmsg,
__DEFEX_socketcall,
__DEFEX_rt_sigsuspend,
__DEFEX_rt_sigpending,
__DEFEX_rt_sigaction,
__DEFEX_signal,
__DEFEX_remap_file_pages,
__DEFEX_ppoll,
__DEFEX_dup,
__DEFEX_dup3,
__DEFEX_eventfd2,
__DEFEX_timerfd_create,
__DEFEX_timerfd_gettime,
__DEFEX_timerfd_settime,
__DEFEX_epoll_create1,
__DEFEX_epoll_ctl,
__DEFEX_epoll_pwait,
__DEFEX_rt_sigtimedwait,
__DEFEX_clone,
__DEFEX_execve, // -> included to the source
__DEFEX_setxattr,
__DEFEX_lsetxattr,
__DEFEX_fsetxattr,
__DEFEX_removexattr,
__DEFEX_lremovexattr,
__DEFEX_fremovexattr,
__DEFEX_inotify_init1,
__DEFEX_syscalls,
};
struct local_syscall_struct {
enum defex_local_syscall local_syscall;
long int err_code;
};
const struct local_syscall_struct *get_local_syscall(int syscall_no);
const struct local_syscall_struct *get_local_syscall_compat(int syscall_no);
int syscall_local2global(int syscall_no);
#endif /* __CONFIG_SECURITY_DEFEX_CATCH_LIST_H */

View file

@ -1,56 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef __CONFIG_SECURITY_DEFEX_CONFIG_H
#define __CONFIG_SECURITY_DEFEX_CONFIG_H
/* MAX VALUES */
#define MAX_LEN 256
#define MAX_EXCEPTIONS 1024
#define MAX_PROTECTED 1024
#ifdef DEFEX_PERMISSIVE_PED
#define GLOBAL_PED_STATUS (FEATURE_CHECK_CREDS | FEATURE_CHECK_CREDS_SOFT)
#else
#define GLOBAL_PED_STATUS FEATURE_CHECK_CREDS
#endif
#ifdef DEFEX_PERMISSIVE_INT
#define GLOBAL_INTEGRITY_STATUS (FEATURE_INTEGRITY | FEATURE_INTEGRITY_SOFT)
#else
#define GLOBAL_INTEGRITY_STATUS FEATURE_INTEGRITY
#endif
#ifdef DEFEX_PERMISSIVE_SP
#define GLOBAL_SAFEPLACE_STATUS (FEATURE_SAFEPLACE | FEATURE_SAFEPLACE_SOFT)
#else
#define GLOBAL_SAFEPLACE_STATUS FEATURE_SAFEPLACE
#endif
#ifdef DEFEX_PERMISSIVE_TM
#define GLOBAL_TRUSTED_MAP_STATUS (FEATURE_TRUSTED_MAP | FEATURE_TRUSTED_MAP_SOFT)
#else
#define GLOBAL_TRUSTED_MAP_STATUS FEATURE_TRUSTED_MAP
#endif
#ifdef DEFEX_PERMISSIVE_IM
#define GLOBAL_IMMUTABLE_STATUS (FEATURE_IMMUTABLE | FEATURE_IMMUTABLE_SOFT)
#else
#define GLOBAL_IMMUTABLE_STATUS FEATURE_IMMUTABLE
#endif
/* Uncomment for Kernels, that require it */
#define STRICT_UID_TYPE_CHECKS 1
#if defined(DEFEX_PED_ENABLE) || defined(DEFEX_SAFEPLACE_ENABLE) || defined(DEFEX_TRUSTED_MAP_ENABLE) || defined(DEFEX_IMMUTABLE_ENABLE)
#define DEFEX_FEATURE_ENABLE
#endif /* DEFEX_PED_ENABLE || DEFEX_SAFEPLACE_ENABLE || DEFEX_TRUSTED_MAP_ENABLE || DEFEX_IMMUTABLE_ENABLE */
int defex_get_features(void);
#endif /* CONFIG_SECURITY_DEFEX_CONFIG_H */

View file

@ -1,70 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef __DEFEX_DEBUG_H
#define __DEFEX_DEBUG_H
#define DBG_SETUID 0
#define DBG_SET_FSUID 1
#define DBG_SETGID 2
#define DBG_SET_PE_STATUS 3
#define DBG_SET_IM_STATUS 4
#define DBG_SET_SP_STATUS 5
#define DBG_SET_INT_STATUS 6
#define DBG_GET_LOG 7
#define MAX_DATA_LEN 300
#define DEFEX_LOG_TAG "[DEFEX] "
#define DEFEX_LOG_BUF_SIZE (PAGE_SIZE << 0)
#define DEFEX_LOG_BUF_MASK (DEFEX_LOG_BUF_SIZE - 1)
#define DEFEX_LOG_LEVEL_MASK (MSG_CRIT | MSG_ERR | MSG_WARN | MSG_INFO | MSG_DEBUG | MSG_TIMEOFF | MSG_BLOB)
enum defex_log_level {
MSG_CRIT = 1,
MSG_ERR = 2,
MSG_WARN = 4,
MSG_INFO = 8,
MSG_DEBUG = 16,
MSG_TIMEOFF = 32,
MSG_BLOB = 64
};
int defex_create_debug(struct kset *defex_kset);
void blob(const char *title, const char *buffer, const size_t bufLen, const int lineSize);
#ifdef DEFEX_DEBUG_ENABLE
#define defex_log_crit(fmt, ...) defex_print_msg(MSG_CRIT, fmt, ##__VA_ARGS__)
#define defex_log_err(fmt, ...) defex_print_msg(MSG_ERR, fmt, ##__VA_ARGS__)
#define defex_log_warn(fmt, ...) defex_print_msg(MSG_WARN, fmt, ##__VA_ARGS__)
#define defex_log_info(fmt, ...) defex_print_msg(MSG_INFO, fmt, ##__VA_ARGS__)
#define defex_log_debug(fmt, ...) defex_print_msg(MSG_DEBUG, fmt, ##__VA_ARGS__)
#define defex_log_timeoff(fmt, ...) defex_print_msg(MSG_TIMEOFF, fmt, ##__VA_ARGS__)
#define defex_log_blob(fmt, ...) defex_print_msg(MSG_BLOB, fmt, ##__VA_ARGS__)
void defex_print_msg(const enum defex_log_level msg_type, const char *format, ...);
#else
#define defex_log_crit(fmt, ...) pr_crit(DEFEX_LOG_TAG fmt "\n", ##__VA_ARGS__)
#define defex_log_err(fmt, ...) pr_err(DEFEX_LOG_TAG fmt "\n", ##__VA_ARGS__)
#define defex_log_warn(fmt, ...) pr_warn(DEFEX_LOG_TAG fmt "\n", ##__VA_ARGS__)
#define defex_log_info(fmt, ...) pr_info(DEFEX_LOG_TAG fmt "\n", ##__VA_ARGS__)
#define defex_log_debug(fmt, ...) pr_debug(DEFEX_LOG_TAG fmt "\n", ##__VA_ARGS__)
#define defex_log_timeoff(fmt, ...) pr_info(DEFEX_LOG_TAG fmt "\n", ##__VA_ARGS__)
#define defex_log_blob(fmt, ...) pr_crit(fmt "\n", ##__VA_ARGS__)
#endif /* DEFEX_DEBUG_ENABLE */
#ifdef DEFEX_LOG_BUFFER_ENABLE
void log_buffer_flush(void);
#endif /* DEFEX_LOG_BUFFER_ENABLE */
#ifdef DEFEX_SHOW_RULES_ENABLE
int defex_show_structure(void *packed_rules, int rules_size);
#endif /* DEFEX_SHOW_RULES_ENABLE */
#endif /* __DEFEX_DEBUG_H */

View file

@ -1,230 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef __CONFIG_SECURITY_DEFEX_INTERNAL_H
#define __CONFIG_SECURITY_DEFEX_INTERNAL_H
#include <linux/cred.h>
#include <linux/fs.h>
#include <linux/hashtable.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/vmalloc.h>
#include <linux/defex.h>
#include "defex_config.h"
#ifdef DEFEX_KUNIT_ENABLED
#include <kunit/mock.h>
#endif
#define DEFEX_MAJOR_VERSION 2
#define DEFEX_MINOR_VERSION 6
#define DEFEX_REVISION "rel"
/* DEFEX Features */
#define FEATURE_NONE 0
#define FEATURE_CHECK_CREDS (1 << 0)
#define FEATURE_CHECK_CREDS_SOFT (1 << 1)
#define FEATURE_JAILHOUSE (1 << 2) /* reserved for future use */
#define FEATURE_JAILHOUSE_SOFT (1 << 3) /* reserved for future use */
#define FEATURE_RESTRICT_SYSCALL (1 << 4) /* reserved for future use */
#define FEATURE_RESTRICT_SYSCALL_SOFT (1 << 5) /* reserved for future use */
#define FEATURE_IMMUTABLE (1 << 6)
#define FEATURE_IMMUTABLE_SOFT (1 << 7)
#define FEATURE_SAFEPLACE (1 << 8)
#define FEATURE_SAFEPLACE_SOFT (1 << 9)
#define FEATURE_FIVE (1 << 10) /* reserved for future use */
#define FEATURE_FIVE_SOFT (1 << 11) /* reserved for future use */
#define FEATURE_TRUSTED_MAP (1 << 12)
#define FEATURE_TRUSTED_MAP_SOFT (1 << 13)
#define FEATURE_INTEGRITY (1 << 14)
#define FEATURE_INTEGRITY_SOFT (1 << 15)
#define FEATURE_CLEAR_ALL (0xFF0000)
#define DEFEX_ALLOW 0
#define DEFEX_DENY 1
#define DEFEX_OK 0
#define DEFEX_NOK 1
#define DEFEX_STARTED 1
/* -------------------------------------------------------------------------- */
/* Integrity feature */
/* -------------------------------------------------------------------------- */
#define DEFEX_INTEGRITY_FAIL (1 << 1)
/* -------------------------------------------------------------------------- */
/* PrivEsc feature */
/* -------------------------------------------------------------------------- */
#ifdef STRICT_UID_TYPE_CHECKS
#define CHECK_ROOT_CREDS(x) (uid_eq((x)->uid, GLOBAL_ROOT_UID) || \
gid_eq((x)->gid, GLOBAL_ROOT_GID) || \
uid_eq((x)->euid, GLOBAL_ROOT_UID) || \
gid_eq((x)->egid, GLOBAL_ROOT_GID))
#define GLOBAL_SYS_UID KUIDT_INIT(1000)
#define GLOBAL_SYS_GID KGIDT_INIT(1000)
#define CHECK_SYS_CREDS(x) (uid_eq((x)->uid, GLOBAL_SYS_UID) || \
gid_eq((x)->gid, GLOBAL_SYS_GID) || \
uid_eq((x)->euid, GLOBAL_SYS_UID) || \
gid_eq((x)->egid, GLOBAL_SYS_GID))
#define uid_get_value(x) (x.val)
#define uid_set_value(x, v) x.val = v
#else
#define CHECK_ROOT_CREDS(x) (((x)->uid == 0) || ((x)->gid == 0) || \
((x)->euid == 0) || ((x)->egid == 0))
#define uid_get_value(x) (x)
#define uid_set_value(x, v) (x = v)
#endif /* STRICT_UID_TYPE_CHECKS */
#define CRED_FLAGS_PROOT (1 << 0) /* parent is root */
#define CRED_FLAGS_MAIN_UPDATED (1 << 1) /* main thread's permission updated */
#define CRED_FLAGS_SUB_UPDATED (1 << 2) /* sub thread's permission updated */
#define GET_CREDS(ids_ptr, cred_data_ptr) do { uid = (ids_ptr)->uid; \
fsuid = (ids_ptr)->fsuid; \
egid = (ids_ptr)->egid; \
cred_flags = (cred_data_ptr)->cred_flags; } while(0)
#define SET_CREDS(ids_ptr, cred_data_ptr) do { (ids_ptr)->uid = uid; \
(ids_ptr)->fsuid = fsuid; \
(ids_ptr)->egid = egid; \
(cred_data_ptr)->cred_flags |= cred_flags; } while(0)
extern unsigned char global_privesc_status;
void get_task_creds(struct task_struct *p, unsigned int *uid_ptr, unsigned int *fsuid_ptr, unsigned int *egid_ptr, unsigned short *cred_flags_ptr);
int set_task_creds(struct task_struct *p, unsigned int uid, unsigned int fsuid, unsigned int egid, unsigned short cred_flags);
void set_task_creds_tcnt(struct task_struct *p, int addition);
int is_task_creds_ready(void);
/* -------------------------------------------------------------------------- */
/* Integrity feature */
/* -------------------------------------------------------------------------- */
extern unsigned char global_integrity_status;
/* -------------------------------------------------------------------------- */
/* SafePlace feature */
/* -------------------------------------------------------------------------- */
extern unsigned char global_safeplace_status;
/* -------------------------------------------------------------------------- */
/* Immutable feature */
/* -------------------------------------------------------------------------- */
extern unsigned char global_immutable_status;
/* -------------------------------------------------------------------------- */
/* Trusted Map feature */
/* -------------------------------------------------------------------------- */
extern unsigned char global_trusted_map_status;
enum trusted_map_status {
DEFEX_TM_ENFORCING_MODE = (1 << 0),
DEFEX_TM_PERMISSIVE_MODE = (1 << 1),
DEFEX_TM_DEBUG_VIOLATIONS = (1 << 2),
DEFEX_TM_DEBUG_CALLS = (1 << 3),
DEFEX_TM_LAST_STATUS = (1 << 4) - 1
};
static inline int defex_tm_mode_enabled(int mode_flag)
{
return global_trusted_map_status & mode_flag;
}
struct defex_context;
int defex_trusted_map_lookup(struct defex_context *dc, int argc, void *argv);
/* -------------------------------------------------------------------------- */
/* Common Helper API */
/* -------------------------------------------------------------------------- */
struct defex_context {
int syscall_no;
struct task_struct *task;
struct file *process_file;
struct file *target_file;
const struct path *process_dpath;
const struct path *target_dpath;
char *process_name;
char *target_name;
char *target_name_buff;
char *process_name_buff;
/* NB: cred must be the last field */
struct cred *cred;
};
extern const char unknown_file[];
struct file *local_fopen(const char *fname, int flags, umode_t mode);
int local_fread(struct file *f, loff_t offset, void *ptr, unsigned long bytes);
int init_defex_context(struct defex_context *dc, int syscall, struct task_struct *p, struct file *f);
void release_defex_context(struct defex_context *dc);
struct file *get_dc_process_file(struct defex_context *dc);
const struct path *get_dc_process_dpath(struct defex_context *dc);
char *get_dc_process_name(struct defex_context *dc);
const struct path *get_dc_target_dpath(struct defex_context *dc);
char *get_dc_target_name(struct defex_context *dc);
struct file *defex_get_source_file(struct task_struct *p);
char *defex_get_filename(struct task_struct *p);
char* defex_resolve_filename(const char *name, char **out_buff);
int defex_files_identical(const struct file *f1, const struct file *f2);
static inline void safe_str_free(void *ptr)
{
if (ptr && ptr != unknown_file)
kfree(ptr);
}
/* -------------------------------------------------------------------------- */
/* Defex lookup API */
/* -------------------------------------------------------------------------- */
int rules_lookup(const char *target_file, int attribute, struct file *f);
/* -------------------------------------------------------------------------- */
/* Defex init API */
/* -------------------------------------------------------------------------- */
int __init defex_init_sysfs(void);
void __init creds_fast_hash_init(void);
int __init do_load_rules(void);
/* -------------------------------------------------------------------------- */
/* Defex debug API */
/* -------------------------------------------------------------------------- */
int immutable_status_store(const char *status_str);
int privesc_status_store(const char *status_str);
int safeplace_status_store(const char *status_str);
int integrity_status_store(const char *status_str);
extern bool boot_state_recovery __ro_after_init;
#ifdef DEFEX_DEPENDING_ON_OEMUNLOCK
extern bool boot_state_unlocked __ro_after_init;
extern int warranty_bit __ro_after_init;
#else
#define boot_state_unlocked (0)
#define warranty_bit (0)
#endif /* DEFEX_DEPENDING_ON_OEMUNLOCK */
#endif /* CONFIG_SECURITY_DEFEX_INTERNAL_H */

View file

@ -1,73 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef __DEFEX_RULES_H
#define __DEFEX_RULES_H
#ifdef DEFEX_TRUSTED_MAP_ENABLE
#include "ptree.h"
#endif
#define STATIC_RULES_MAX_STR 32
#define INTEGRITY_LENGTH 32
#define FEATURE_NAME_MAX_STR 32
#define GET_ITEM_OFFSET(item_ptr) (((char *)item_ptr) - ((char *)defex_packed_rules))
#define GET_ITEM_PTR(offset, base_ptr) ((struct rule_item_struct *)(((char *)base_ptr) + (offset)))
enum feature_types {
feature_is_file = 1,
feature_for_recovery = 2,
feature_ped_path = 4,
feature_ped_exception = 8,
feature_ped_status = 16,
feature_safeplace_path = 32,
feature_safeplace_status = 64,
feature_immutable_path_open = 128,
feature_immutable_path_write = 256,
feature_immutable_src_exception = 512,
feature_immutable_status = 1024,
feature_umhbin_path = 2048,
feature_trusted_map_status = 4096,
feature_integrity_check = 8192
};
struct feature_match_entry {
char feature_name[FEATURE_NAME_MAX_STR];
int feature_num;
};
struct static_rule {
unsigned int feature_type;
char rule[STATIC_RULES_MAX_STR];
};
struct rule_item_struct {
unsigned short int next_level;
union {
struct {
unsigned short int next_file;
unsigned short int feature_type;
} __attribute__((packed));
unsigned int data_size;
} __attribute__((packed));
unsigned char size;
#ifdef DEFEX_INTEGRITY_ENABLE
unsigned char integrity[INTEGRITY_LENGTH];
#endif /* DEFEX_INTEGRITY_ENABLE */
char name[0];
} __attribute__((packed));
int check_rules_ready(void);
#ifdef DEFEX_TRUSTED_MAP_ENABLE
/* "Header" for DTM's dynamically loaded policy */
extern struct PPTree dtm_tree;
#endif
#endif /* __DEFEX_RULES_H */

View file

@ -1,16 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef __DEFEX_SIGN_H
#define __DEFEX_SIGN_H
#define SIGN_SIZE 256
int defex_rules_signature_check(const char *rules_buffer, unsigned int rules_data_size, unsigned int *rules_size);
#endif /* __DEFEX_SIGN_H */

View file

@ -1,57 +0,0 @@
#ifndef __INCLUDE_TAILER_H__
#define __INCLUDE_TAILER_H__
/* Functions for managing a "tailer file", which is similar to a tar
* archive but, in order to support ordinary unadorned files
* - stores metainformation after each archived file
* - stores a single magic number at the very end.
*/
/* Magic number, occurs only once at the end of tailer file. Should it
* change, be sure to update TAIL_MAGIC_LEN
*/
#define TAIL_MAGIC "#TAIL_GUARD#"
#define TAIL_MAGIC_LEN 12
/* Maximum length of title associated with a stored file. Arbitrary, but
* should it increase, metainfo size (currently 1 byte) must change
* accordingly
*/
#define TAIL_MAX_TITLE_LENGTH 255
/* Each file's metainfo entry comprises (version 1,0):
* - offset where actual contents start: 4-byte big-endian
* - size of contents in bytes: 4-byte big-endian
* - title, up to TAIL_MAX_TITLE_LENGTH bytes, non 0-terminated
* - title length, 1 byte
* - major/minor version number, 1 byte each
*
* A tailer file is either an unadorned one or a linked list of content+
* metainfo entries which goes backwards from the magic number.
*/
/* Functions for handling tailer data as memory buffers */
/* If a memory buffer <p> with <size> given ends with the tailer magic
* suffix, returns its offset, else returns -1
*/
extern long defex_tailerp_has_suffix(const unsigned char *p, long size);
/* Given buffer <p> of <size> given, returns address of last occurrence
* of contents of given <title> (and if <sizep> sets *<sizep> to
* contents size), or 0 if <p> is unadorned has no such
* occurrences
*/
extern const unsigned char *defex_tailerp_find(const unsigned char *p, long size,
const char *title, long *sizep);
/* Given buffer <p> with <size> bytes, returns 0 if unadorned. Else,
* executes <task> for each entry, from last to first, passing arguments
* <title> (unterminated), title length, absolute <start> address and
* <size> in bytes, plus <data>. Terminates immediately if <task> returns
* negative. Returns last result of <task>.
*/
extern int defex_tailerp_iterate(const unsigned char *p, long size,
int (*task)(const char *title, int titleLen,
const unsigned char *start,
long size, void *data),
void *data);
#endif

View file

@ -1,87 +0,0 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef _DEFEX_TEST_H
#define _DEFEX_TEST_H
#ifdef DEFEX_KUNIT_ENABLED
#include <kunit/mock.h>
#include <linux/types.h>
#include <linux/sysfs.h>
/* -------------------------------------------------------------------------- */
/* defex_debug */
/* -------------------------------------------------------------------------- */
extern struct kobj_attribute debug_attribute;
extern int set_user(struct cred *new_cred);
extern int set_cred(int target_id, int new_val);
extern ssize_t debug_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count);
extern ssize_t debug_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf);
/* -------------------------------------------------------------------------- */
/* defex_immutable */
/* -------------------------------------------------------------------------- */
extern int immutable_status_store(const char *status_str);
/* -------------------------------------------------------------------------- */
/* defex_priv */
/* -------------------------------------------------------------------------- */
extern int privesc_status_store(const char *status_str);
/* -------------------------------------------------------------------------- */
/* defex_safeplace */
/* -------------------------------------------------------------------------- */
extern int safeplace_status_store(const char *status_str);
/* -------------------------------------------------------------------------- */
/* defex_main */
/* -------------------------------------------------------------------------- */
struct defex_context;
extern struct task_struct *get_parent_task(const struct task_struct *p);
#ifdef DEFEX_DSMS_ENABLE
extern void defex_report_violation(const char *violation, uint64_t counter, struct defex_context *dc,
uid_t stored_uid, uid_t stored_fsuid, uid_t stored_egid, int case_num);
#endif
#ifdef DEFEX_SAFEPLACE_ENABLE
extern long kill_process(struct task_struct *p);
#endif
#ifdef DEFEX_PED_ENABLE
extern long kill_process_group(struct task_struct *p, int tgid, int pid);
extern int task_defex_is_secured(struct defex_context *dc);
extern int at_same_group(unsigned int uid1, unsigned int uid2);
extern int at_same_group_gid(unsigned int gid1, unsigned int gid2);
#ifdef DEFEX_LP_ENABLE
extern int lower_adb_permission(struct defex_context *dc, unsigned short cred_flags);
#endif /* DEFEX_LP_ENABLE */
extern int task_defex_check_creds(struct defex_context *dc);
#endif /* DEFEX_PED_ENABLE */
#ifdef DEFEX_SAFEPLACE_ENABLE
extern int task_defex_safeplace(struct defex_context *dc);
#endif /* DEFEX_SAFEPLACE_ENABLE */
#ifdef DEFEX_IMMUTABLE_ENABLE
extern int task_defex_src_exception(struct defex_context *dc);
extern int task_defex_immutable(struct defex_context *dc, int attribute);
#endif /* DEFEX_IMMUTABLE_ENABLE */
#endif /* DEFEX_KUNIT_ENABLED */
#endif /* _DEFEX_TEST_H */

View file

@ -1,171 +0,0 @@
/*
* Copyright (c) 2021-2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef __PTREE_H__
/* A "P-tree" is a n-ary search tree whose keys are strings. A p-tree
* node may be associated with data like a string, a byte array or
* an entry point for an additional search (unrelated to the node's
* children).
*
* P-trees are represented in two variants:
* - a "build" data structure with traditional structs and pointers, used for
* building, converting, merging or exporting
* - a "portable" format packed with integer indices instead of pointers,
* stored as a byte array and used (read-only) by target applications.
* In both, all strings are stored at a dictionary and represented in nodes
* by indices, therefore child data have constant size; in addition, child
* lists are sorted, allowing efficient search.
*/
/* Masks for data types. Should any be created, PTREE_DATA_MASK must
* be updated accordingly
*/
#define PTREE_DATA_BYTES 1 /* byte array */
#define PTREE_DATA_STRING 2 /* zero-terminated string */
#define PTREE_DATA_PATH 4 /* subpath */
#define PTREE_DATA_INT1 8 /* byte */
#define PTREE_DATA_INT2 16 /* 2-byte short */
#define PTREE_DATA_INT4 32 /* 4-byte int */
#define PTREE_DATA_BITA 64 /* bit */
/* Bit A is stored directly in the bit below */
#define PTREE_DATA_BITA_MASK 128
#define PTREE_DATA_MASK 255 /* ORs all PTREE_DATA masks */
/* Modifiers for search behavior */
#define PTREE_FIND_CONTINUE 256 /* pptree_find should continue from
* previous result, if any
*/
#define PTREE_FIND_PEEK 512 /* a successful search does not advance
* the context, therefore the next search
* will start from the same point
*/
#define PTREE_FIND_PEEKED 1024 /* go to where a previous search would have
* gone had it not used PTREE_FIND_PEEKED
*/
/* ***************************************************************************
* Declarations needed for _using_ exported P-trees
* ***************************************************************************/
/* Header for portable P-tree. This is not the binary format, rather after
* loading it caches the tree's details.
*/
struct PPTree {
const unsigned char *data;
struct {
int fullSize;
int size;
char indexSize;
const unsigned char *table;
} sTable;
struct {
int fullSize;
int size;
char indexSize;
const unsigned char *table;
} bTable;
struct {
char offsetSize;
char childCountSize;
const unsigned char *root;
} nodes;
char allocated;
};
/* Magic number (fixed portion, plus two version bytes) */
#define PPTREE_MAGIC "PPTree-\1\0"
#define PPTREE_MAGIC_FIXEDSIZE 7
/* Sets a byte array as a p-tree's data. Returns 0 if successful. */
extern int pptree_set_data(struct PPTree *tree, const unsigned char *data);
/* Releases pptree data, if needed */
extern void pptree_free(struct PPTree *tree);
/* Context data for portable p-tree operations, especially searching.
* Used for both input (key data) and output (result's place and data)
* Search starts from root, or,
* if .types is PPTREE_DATA_PATH and there's a subpath, from .value.childPath
* if .types is PPTREE_FIND_CONTINUE, from latest sucessful search
* If .types contains PTREE_DATA_PEEK, context does not advance even if
* search is successful. It will advance (and no search will be done) if next
* search include PTREE_DATA_PEEKED.
*/
struct PPTreeContext {
int types;
struct {
struct {
const unsigned char *bytes;
int length;
} bytearray;
const char *string;
int childPath;
int int1;
int int2;
int int4;
unsigned char bits;
} value;
const unsigned char *last, *lastPeeked;
unsigned int childCount;
};
/* Search for given path. Return 0 if not found, 1 otherwise
* (and fills in *ctx).
* See PPTreeContext for where the search starts.
*/
extern int pptree_find(const struct PPTree *tree,
const char **path, int pathLen,
struct PPTreeContext *ctx);
/* Maximum key length, mostly an arbitrary limit against DoS */
#define PTREE_FINDPATH_MAX 8000
/* Search for a given path.
* Similar to pptree_find, but splits <path> at every occurrence of <delim>
* (unless delim is 0). In kernelspace, returns 0 if <path> length exceeds
* PTREE_FINDPATH_MAX.
*/
extern int pptree_find_path(const struct PPTree *tree, const char *path,
char delim, struct PPTreeContext *ctx);
/* Returns number of children.
* See PPTreeContext for which is the parent node.
*/
extern int pptree_child_count(const struct PPTree *tree,
struct PPTreeContext *ctx);
/* Iterates on immediate children.
* See PPTreeContext for iteration root.
* Executes <f> for all children until <f> returns nonzero. Returns
* last return of <f>.
*/
extern int pptree_iterate_children(const struct PPTree *tree,
struct PPTreeContext *ctx,
int (*f)(const struct PPTree *tree,
const char *name,
const struct PPTreeContext *itemData,
void *data),
void *data);
/* Iterate on subpaths.
* See PPTreeContext for iteration root.
* Executes <f> for all subpaths ending on a leaf, stopping if <f>
* returns nonzero.
* Returns last result of <f>
*/
extern int pptree_iterate_paths(const struct PPTree *tree,
struct PPTreeContext *ctx,
int (*f)(const struct PPTree *tree,
const char **path,
int pathLen, void *data),
const char **path, int maxDepth,
void *data);
/* Dumps to stdout in human-readable format */
extern void pptree_dump(const struct PPTree *tree);
/* Maximum number of bytes for counters (practical reasonable limit) */
#define UPPER_COUNT_SIZE 4
#define __PTREE_H__
#endif

View file

@ -1,893 +0,0 @@
/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "include/defex_rules.h"
#define SAFE_STRCOPY(dst, src) do { strncpy(dst, src, sizeof(dst)); dst[sizeof(dst) - 1] = 0; } while(0)
const struct feature_match_entry feature_match[] = {
{"feature_safeplace_path", feature_safeplace_path},
{"feature_ped_exception", feature_ped_exception},
{"feature_immutable_path_open", feature_immutable_path_open},
{"feature_immutable_path_write", feature_immutable_path_write},
{"feature_immutable_src_exception", feature_immutable_src_exception},
{"feature_umhbin_path", feature_umhbin_path},
{"feature_integrity_check", feature_integrity_check},
};
const int feature_match_size = sizeof(feature_match) / sizeof(feature_match[0]);
struct file_list_item {
char file_name[PATH_MAX];
#ifdef DEFEX_INTEGRITY_ENABLE
char integrity[INTEGRITY_LENGTH * 2 + 1];
#endif /* DEFEX_INTEGRITY_ENABLE */
int is_recovery;
};
struct rule_item_struct *defex_packed_rules;
int packfiles_count, packfiles_size;
struct file_list_item *file_list = NULL;
int file_list_count = 0;
#ifndef DEFEX_DEBUG_ENABLE
int debug_ifdef_is_active = 0;
void process_debug_ifdef(const char *src_str);
#endif
/* Show rules vars */
const char header_name[16] = {"DEFEX_RULES_FILE"};
static char work_path[512];
static int global_data_size;
/* Suplementary functions for packing rules */
struct rule_item_struct *create_file_item(const char *name, int l);
struct rule_item_struct *add_file_item(struct rule_item_struct *base, const char *name, int l);
struct rule_item_struct *lookup_dir(struct rule_item_struct *base, const char *name, int l, int for_recovery);
struct rule_item_struct *add_file_path(const char *file_path, int for_recovery);
struct rule_item_struct *addline2tree(char *src_line, enum feature_types feature);
char *extract_rule_text(const char *src_line);
int lookup_tree(const char *file_path, int attribute, int for_recovery);
int store_tree(FILE *f, FILE *f_bin);
#ifdef DEFEX_INTEGRITY_ENABLE
/* Transfer string to hex */
unsigned char ascii_to_hex(unsigned char input);
int string_to_hex(unsigned char *input, size_t inputLen, unsigned char *output);
char null_integrity[INTEGRITY_LENGTH * 2 + 1];
#endif /* DEFEX_INTEGRITY_ENABLE */
/* Suplementary functions for reducing rules */
int remove_substr(char *str, const char *part);
void trim_cr_lf(char *str);
char* remove_redundant_chars(char *str);
int load_file_list(const char *name);
int lookup_file_list(const char *rule, int for_recovery);
/* Main processing functions */
int reduce_rules(const char *source_rules_file, const char *reduced_rules_file, const char *list_file);
int pack_rules(const char *source_rules_file, const char *packed_rules_file, const char *packed_rules_binfile);
struct rule_item_struct *create_file_item(const char *name, int l)
{
struct rule_item_struct *item;
unsigned int offset;
if (!name)
l = 0;
offset = packfiles_size;
packfiles_size += (sizeof(struct rule_item_struct) + l);
packfiles_count++;
item = GET_ITEM_PTR(offset, defex_packed_rules);
item->next_file = 0;
item->next_level = 0;
item->feature_type = 0;
item->size = l;
#ifdef DEFEX_INTEGRITY_ENABLE
memset(item->integrity, 0, INTEGRITY_LENGTH);
#endif /* DEFEX_INTEGRITY_ENABLE */
if (l)
memcpy(item->name, name, l);
return item;
}
struct rule_item_struct *add_file_item(struct rule_item_struct *base, const char *name, int l)
{
struct rule_item_struct *item, *new_item = NULL;
if (!base)
return new_item;
new_item = create_file_item(name, l);
if (!base->next_level) {
base->next_level = GET_ITEM_OFFSET(new_item);
} else {
item = GET_ITEM_PTR(base->next_level, defex_packed_rules);
while(item->next_file) {
item = GET_ITEM_PTR(item->next_file, defex_packed_rules);
}
item->next_file = GET_ITEM_OFFSET(new_item);
}
return new_item;
}
struct rule_item_struct *lookup_dir(struct rule_item_struct *base, const char *name, int l, int for_recovery)
{
struct rule_item_struct *item = NULL;
unsigned int offset;
if (!base || !base->next_level)
return item;
item = GET_ITEM_PTR(base->next_level, defex_packed_rules);
do {
if ((!(item->feature_type & feature_is_file)
|| (!!(item->feature_type & feature_for_recovery)) == for_recovery)
&& item->size == l
&& !memcmp(name, item->name, l)) return item;
offset = item->next_file;
item = GET_ITEM_PTR(offset, defex_packed_rules);
} while(offset);
return NULL;
}
struct rule_item_struct *add_file_path(const char *file_path, int for_recovery)
{
const char *ptr, *next_separator;
struct rule_item_struct *base, *cur_item = NULL;
int l;
if (!file_path || *file_path != '/')
return NULL;
if (!defex_packed_rules) {
packfiles_count = 0;
packfiles_size = 0;
defex_packed_rules = calloc(sizeof(struct rule_item_struct), 100 * 1024);
if (!defex_packed_rules) {
printf("WARNING: Can not create the new item!\n");
exit(-1);
}
create_file_item(header_name, sizeof(header_name));
}
base = defex_packed_rules;
ptr = file_path + 1;
do {
next_separator = strchr(ptr, '/');
if (!next_separator)
l = strlen(ptr);
else
l = next_separator - ptr;
if (!l)
return NULL; /* two slashes in sequence */
cur_item = lookup_dir(base, ptr, l, for_recovery);
if (!cur_item) {
cur_item = add_file_item(base, ptr, l);
/* slash wasn't found, it's a file */
if (!next_separator) {
cur_item->feature_type |= feature_is_file;
if (for_recovery)
cur_item->feature_type |= feature_for_recovery;
}
}
base = cur_item;
ptr += l;
if (next_separator)
ptr++;
} while(*ptr);
return cur_item;
}
int lookup_tree(const char *file_path, int attribute, int for_recovery)
{
const char *ptr, *next_separator;
struct rule_item_struct *base, *cur_item = NULL;
int l;
if (!file_path || *file_path != '/' || !defex_packed_rules)
return 0;
base = defex_packed_rules;
ptr = file_path + 1;
do {
next_separator = strchr(ptr, '/');
if (!next_separator)
l = strlen(ptr);
else
l = next_separator - ptr;
if (!l)
return 0;
cur_item = lookup_dir(base, ptr, l, for_recovery);
if (!cur_item)
break;
if (cur_item->feature_type & attribute)
return 1;
base = cur_item;
ptr += l;
if (next_separator)
ptr++;
} while(*ptr);
return 0;
}
char *extract_rule_text(const char *src_line)
{
char *start_ptr, *end_ptr;
start_ptr = strchr(src_line, '\"');
if (start_ptr) {
start_ptr++;
end_ptr = strchr(start_ptr, '\"');
if (end_ptr) {
*end_ptr = 0;
return start_ptr;
}
}
return NULL;
}
#ifdef DEFEX_INTEGRITY_ENABLE
unsigned char ascii_to_hex(unsigned char input)
{
if (input >= 0x30 && input <=0x39)
return input - 0x30;
else if (input >= 0x41 && input <= 0x46)
return input - 0x37;
else if (input >= 0x61 && input <= 0x66)
return input - 0x57;
else
return 0xFF;
}
int string_to_hex(unsigned char *input, size_t inputLen, unsigned char *output)
{
unsigned char convert1, convert2;
size_t i;
if (input == NULL || output == NULL)
return 0;
// Check input is a paired value.
if (inputLen % 2 != 0)
return 0;
// Convert ascii code to hexa.
for (i = 0; i < inputLen / 2; i++) {
convert1 = ascii_to_hex(input[2*i]);
convert2 = ascii_to_hex(input[2*i+1]);
if (convert1 == 0xFF || convert2 == 0xFF)
return 0;
output[i] = (char)((convert1 << 4) | convert2);
}
return 1;
}
#endif /* DEFEX_INTEGRITY_ENABLE */
struct rule_item_struct *addline2tree(char *src_line, enum feature_types feature)
{
struct rule_item_struct *item = NULL;
char *str;
#ifdef DEFEX_INTEGRITY_ENABLE
unsigned char *integrity, *n_sign = NULL, *r_sign = NULL;
int value;
#endif /* DEFEX_INTEGRITY_ENABLE */
str = extract_rule_text(src_line);
if (!str)
return NULL;
#ifndef DEFEX_INTEGRITY_ENABLE
item = add_file_path(str, 0);
if (item)
item->feature_type |= feature;
#else
integrity = (unsigned char *)extract_rule_text(str + strnlen(str, PATH_MAX) + 1);
if (integrity) {
n_sign = (unsigned char *)strchr((const char *)integrity, 'N');
r_sign = (unsigned char *)strchr((const char *)integrity, 'R');
}
if (!(n_sign == NULL && r_sign != NULL)) {
item = add_file_path(str, 0);
if (item) {
item->feature_type |= feature;
if (n_sign) {
value = string_to_hex(n_sign + 1, INTEGRITY_LENGTH * 2, item->integrity);
if (!value)
return NULL;
}
}
}
if (r_sign != NULL) {
item = add_file_path(str, 1);
if (item) {
item->feature_type |= feature;
value = string_to_hex(r_sign + 1, INTEGRITY_LENGTH * 2, item->integrity);
if (!value)
return NULL;
}
}
#endif /* DEFEX_INTEGRITY_ENABLE */
return item;
}
int store_tree(FILE *f, FILE *f_bin)
{
unsigned char *ptr = (unsigned char *)defex_packed_rules;
static char work_str[4096];
int i, offset = 0, index = 0;
if (packfiles_size)
defex_packed_rules->data_size = packfiles_size;
work_str[0] = 0;
fprintf(f, "#ifndef DEFEX_RAMDISK_ENABLE\n\n");
fprintf(f, "const unsigned char defex_packed_rules[] = {\n");
for (i = 0; i < packfiles_size; i++) {
if (index)
offset += snprintf(work_str + offset, sizeof(work_str) - offset, ", ");
offset += snprintf(work_str + offset, sizeof(work_str) - offset, "0x%02x", ptr[i]);
index++;
if (index == 16) {
fprintf(f, "\t%s,\n", work_str);
index = 0;
offset = 0;
}
}
if (index)
fprintf(f, "\t%s\n", work_str);
fprintf(f, "};\n");
fprintf(f, "\n#endif /* DEFEX_RAMDISK_ENABLE */\n\n");
fprintf(f, "#define DEFEX_RULES_ARRAY_SIZE\t\t%d\n", packfiles_size);
if (f_bin)
fwrite(defex_packed_rules, 1, packfiles_size, f_bin);
return 0;
}
int remove_substr(char *str, const char *part)
{
int l, part_l, found = 0;
char *ptr;
l = strnlen(str, PATH_MAX - 1);
ptr = strstr(str, part);
if (ptr) {
part_l = strnlen(part, 32) - 1;
memmove(ptr, ptr + part_l, l - (ptr - str) - part_l + 1);
found = 1;
}
return found;
}
void trim_cr_lf(char *str)
{
char *ptr;
/* remove CR or LF at the end */
ptr = strchr(str, '\r');
if (ptr)
*ptr = 0;
ptr = strchr(str, '\n');
if (ptr)
*ptr = 0;
}
char* remove_redundant_chars(char *str)
{
int l;
/* skip hash values in the begin */
str += 65;
trim_cr_lf(str);
l = strnlen(str, PATH_MAX - 1);
/* remove starting dot or space */
while(l && (*str == '.' || *str == ' '))
str++;
return str;
}
int load_file_list(const char *name)
{
int found;
char *str;
FILE *lst_file = NULL;
struct file_list_item *file_list_new;
static char work_str[PATH_MAX*2];
lst_file = fopen(name, "r");
if (!lst_file)
return -1;
while(!feof(lst_file)) {
if (!fgets(work_str, sizeof(work_str), lst_file))
break;
str = remove_redundant_chars(work_str);
if (*str == '/' &&
(!strncmp(str, "/root/", 6) ||
!strncmp(str, "/product/", 9) ||
!strncmp(str, "/recovery/", 10) ||
!strncmp(str, "/system/", 8) ||
!strncmp(str, "/tmp/", 5) ||
!strncmp(str, "/vendor/", 8) ||
#if defined(DEFEX_FACTORY_ENABLE)
!strncmp(str, "/data/", 6) ||
#endif
!strncmp(str, "/apex/", 6) ||
!strncmp(str, "/system_ext/", 12))) {
remove_substr(str, "/root/");
found = remove_substr(str, "/recovery/");
file_list_count++;
file_list_new = realloc(file_list, sizeof(struct file_list_item) * file_list_count);
if (!file_list_new) {
free(file_list);
printf("WARNING: Can not allocate the filelist item!\n");
exit(-1);
}
file_list = file_list_new;
#ifdef DEFEX_INTEGRITY_ENABLE
strncpy(file_list[file_list_count - 1].integrity, work_str, INTEGRITY_LENGTH * 2);
file_list[file_list_count - 1].integrity[INTEGRITY_LENGTH * 2] = 0;
#endif /* DEFEX_INTEGRITY_ENABLE */
SAFE_STRCOPY(file_list[file_list_count - 1].file_name, str);
file_list[file_list_count - 1].is_recovery = found;
}
}
fclose(lst_file);
return 0;
}
int lookup_file_list(const char *rule, int for_recovery)
{
int i;
for (i = 0; i < file_list_count; i++) {
if (file_list[i].is_recovery == for_recovery
&& !strncmp(file_list[i].file_name, rule, strnlen(rule, PATH_MAX))
&& !strncmp(file_list[i].file_name, rule, strnlen(file_list[i].file_name, PATH_MAX)))
return i+1;
}
return 0;
}
#ifndef DEFEX_DEBUG_ENABLE
void process_debug_ifdef(const char *src_str)
{
char *ptr;
ptr = strstr(src_str, "#ifdef DEFEX_DEBUG_ENABLE");
if (ptr) {
while(ptr > src_str) {
ptr--;
if (*ptr != ' ' && *ptr != '\t')
return;
}
debug_ifdef_is_active = 1;
return;
}
ptr = strstr(src_str, "#endif");
if (ptr && debug_ifdef_is_active) {
while(ptr > src_str) {
ptr--;
if (*ptr != ' ' && *ptr != '\t')
return;
}
debug_ifdef_is_active = 0;
return;
}
}
#endif
static int str_to_feature(const char *str)
{
int i;
for (i = 0; i < feature_match_size; i++) {
if (strstr(str, feature_match[i].feature_name)) {
return feature_match[i].feature_num;
}
}
return 0;
}
int reduce_rules(const char *source_rules_file, const char *reduced_rules_file, const char *list_file)
{
int ret_val = -1;
int found_normal = 0, found_recovery = 0;
char *rule;
static char work_str[PATH_MAX*2], tmp_str[PATH_MAX*2];
FILE *src_file = NULL, *dst_file = NULL;
#ifdef DEFEX_INTEGRITY_ENABLE
char *line_end, *integrity_normal;
char *integrity_recovery;
#endif /* DEFEX_INTEGRITY_ENABLE */
src_file = fopen(source_rules_file, "r");
if (!src_file)
return -1;
dst_file = fopen(reduced_rules_file, "wt");
if (!dst_file)
goto do_close2;
if (load_file_list(list_file) != 0)
goto do_close1;
#ifdef DEFEX_INTEGRITY_ENABLE
memset(null_integrity, '0', sizeof(null_integrity) - 1);
null_integrity[sizeof(null_integrity) - 1] = 0;
#endif /* DEFEX_INTEGRITY_ENABLE */
while(!feof(src_file)) {
if (!fgets(work_str, sizeof(work_str), src_file))
break;
if (str_to_feature(work_str)) {
trim_cr_lf(work_str);
SAFE_STRCOPY(tmp_str, work_str);
rule = extract_rule_text(tmp_str);
found_normal = lookup_file_list(rule, 0);
found_recovery = lookup_file_list(rule, 1);
if (rule && !found_normal && !found_recovery && !strstr(work_str, "/* DEFAULT */")) {
printf("removed rule: %s\n", rule);
continue;
}
#ifdef DEFEX_INTEGRITY_ENABLE
integrity_normal = null_integrity;
integrity_recovery = null_integrity;
if (found_normal)
integrity_normal = file_list[found_normal - 1].integrity;
if (found_recovery)
integrity_recovery = file_list[found_recovery - 1].integrity;
line_end = strstr(work_str, "},");
if (line_end) {
*line_end = 0;
line_end += 2;
}
/* Add hash vale after each file path */
if (found_normal || (!found_normal && !found_recovery))
printf("remained rule: %s, %s %s\n",
rule, integrity_normal, (line_end != NULL)?line_end:"");
if (found_recovery)
printf("remained rule: %s, %s %s (R)\n",
rule, integrity_recovery, (line_end != NULL)?line_end:"");
fprintf(dst_file, "%s,\"", work_str);
if (found_normal)
fprintf(dst_file, "N%s", integrity_normal);
if (found_recovery)
fprintf(dst_file, "R%s", integrity_recovery);
fprintf(dst_file, "\"}, %s\n", (line_end != NULL)?line_end:"");
#else
printf("remained rule: %s\n", work_str);
fputs(work_str, dst_file);
fputs("\n", dst_file);
#endif /* DEFEX_INTEGRITY_ENABLE */
} else
fputs(work_str, dst_file);
}
ret_val = 0;
do_close1:
fclose(dst_file);
do_close2:
fclose(src_file);
return ret_val;
}
int pack_rules(const char *source_rules_file, const char *packed_rules_file, const char *packed_rules_binfile)
{
int ret_val = -1;
int feature;
FILE *src_file = NULL, *dst_file = NULL, *dst_binfile = NULL;
static char work_str[PATH_MAX*2];
src_file = fopen(source_rules_file, "r");
if (!src_file) {
printf("Failed to open %s, %s\n", source_rules_file, strerror(errno));
return -1;
}
dst_file = fopen(packed_rules_file, "wt");
if (!dst_file) {
printf("Failed to open %s, %s\n", packed_rules_file, strerror(errno));
goto do_close2;
}
if (packed_rules_binfile) {
dst_binfile = fopen(packed_rules_binfile, "wt");
if (!dst_binfile)
printf("Failed to open %s, %s - Ignore\n", packed_rules_binfile, strerror(errno));
}
while(!feof(src_file)) {
if (!fgets(work_str, sizeof(work_str), src_file))
break;
#ifndef DEFEX_DEBUG_ENABLE
process_debug_ifdef(work_str);
if (!debug_ifdef_is_active) {
#endif
feature = str_to_feature(work_str);
if (feature) {
addline2tree(work_str, feature);
continue;
}
#ifndef DEFEX_DEBUG_ENABLE
}
#endif
}
store_tree(dst_file, dst_binfile);
if (!packfiles_count)
printf("WARNING: Defex packed rules tree is empty!\n");
ret_val = 0;
if (dst_binfile)
fclose(dst_binfile);
fclose(dst_file);
do_close2:
fclose(src_file);
return ret_val;
}
static void feature_to_str(char *str, unsigned short flags)
{
int i;
str[0] = 0;
for (i = 0; i < feature_match_size; i++)
if (flags & feature_match[i].feature_num) {
if (str[0])
strcat(str, ", ");
strcat(str, feature_match[i].feature_name);
}
if (flags & feature_for_recovery) {
if (str[0])
strcat(str, ", ");
strcat(str, "feature_for_recovery");
}
}
static int check_array_size(struct rule_item_struct *ptr)
{
unsigned long offset = (unsigned long)ptr - (unsigned long)defex_packed_rules;
int min_size = (global_data_size < packfiles_size)?global_data_size:packfiles_size;
offset += sizeof(struct rule_item_struct);
if (offset > min_size)
return 1;
offset += ptr->size;
if (offset > min_size)
return 2;
return 0;
}
static int parse_items(struct rule_item_struct *base, int path_length, int level)
{
int l, err, ret = 0;
unsigned int offset;
struct rule_item_struct *child_item;
static char feature_list[128];
if (level > 8) {
printf("Level is too deep\n");
return -1;
}
if (path_length > (sizeof(work_path) - 128)) {
printf("Work path is too long\n");
return -1;
}
while (base) {
err = check_array_size(base);
if (err) {
printf("%s/<?> - out of array bounds\n", work_path);
return -1;
}
l = base->size;
if (!l) {
printf("WARNING: Name field is incorrect, structure error!\n");
return -1;
}
memcpy(work_path + path_length, base->name, l);
l += path_length;
work_path[l] = 0;
offset = base->next_level;
if (offset) {
if (base->feature_type & feature_is_file) {
printf("%s - is a file, but has children, structure error!\n", work_path);
ret = -1;
} else if (base->feature_type != 0) {
feature_to_str(feature_list, base->feature_type);
printf("%s%c - %s\n", work_path,
((base->feature_type & feature_is_file)?' ':'/'), feature_list);
}
child_item = GET_ITEM_PTR(offset, defex_packed_rules);
work_path[l++] = '/';
work_path[l] = 0;
err = check_array_size(child_item);
if (!err) {
err = parse_items(child_item, l, level + 1);
if (err != 0)
return err;
} else {
printf("%s/<?> - out of array bounds\n", work_path);
ret = -1;
}
} else {
feature_to_str(feature_list, base->feature_type);
printf("%s%c - %s\n", work_path,
((base->feature_type & feature_is_file)?' ':'/'), feature_list);
}
work_path[path_length] = 0;
offset = base->next_file;
base = (offset)?GET_ITEM_PTR(offset, defex_packed_rules):NULL;
}
return ret;
}
static int defex_show_structure(void *packed_rules, int rules_size)
{
struct rule_item_struct *base;
int res, offset;
int first_item_size = sizeof(struct rule_item_struct) + sizeof(header_name);
defex_packed_rules = (struct rule_item_struct *)packed_rules;
work_path[0] = '/';
work_path[1] = 0;
packfiles_size = rules_size;
global_data_size = defex_packed_rules->data_size;
printf("Rules binary size: %d\n", packfiles_size);
printf("Rules internal data size: %d\n", global_data_size);
if (global_data_size > packfiles_size)
printf("WARNING: Internal size is bigger than binary size, possible structure error!\n");
if (packfiles_size < first_item_size) {
printf("ERROR: Too short binary size, can't continue!\n");
return -1;
}
if (global_data_size < first_item_size)
printf("WARNING: Too short data size, possible structure error!\n");
if (defex_packed_rules->size != sizeof(header_name))
printf("WARNING: incorrect size field (%d), possible structure error!\n",
(int)defex_packed_rules->size);
if (memcmp(header_name, defex_packed_rules->name, sizeof(header_name)) != 0)
printf("WARNING: incorrect name field, possible structure error!\n");
printf("File List:\n");
offset = defex_packed_rules->next_level;
base = (offset)?GET_ITEM_PTR(offset, defex_packed_rules):NULL;
if (!base) {
printf("- empty list\n");
return 0;
} else if (check_array_size(base)) {
printf("- list is out of array bounds!\n");
return -1;
}
res = parse_items(base, 1, 1);
printf("== End of File List ==\n");
return res;
}
static int parse_packed_bin_file(const char *source_bin_file)
{
struct stat sb;
FILE *policy_file = NULL;
int policy_size;
unsigned char *policy_data = NULL;
if (stat(source_bin_file, &sb) == -1) {
perror("Error");
return -1;
}
policy_size = sb.st_size;
printf("Try to parse file: %s\n", source_bin_file);
policy_file = fopen(source_bin_file, "r");
if (policy_file == NULL) {
perror("Error");
return -1;
}
policy_data = malloc(policy_size);
if (policy_data == NULL) {
perror("Error");
goto exit;
}
if ((fread(policy_data, policy_size, 1, policy_file)) == -1) {
perror("Error");
goto exit;
}
defex_show_structure((void *)policy_data, policy_size);
exit:
free(policy_data);
fclose(policy_file);
return 0;
}
int main(int argc, char **argv)
{
static char param[4][PATH_MAX];
char *src_file = NULL, *packed_file = NULL, *packed_bin_file = NULL;
char *reduced_file = NULL, *list_file = NULL;
int i;
if (argc == 3) {
if (!strncmp(argv[1], "-s", 2)) {
SAFE_STRCOPY(param[0], argv[2]);
src_file = param[0];
parse_packed_bin_file(src_file);
return 0;
}
}
if (argc < 4 || argc > 5) {
printf("Invalid number of arguments\n");
goto show_help;
}
for(i = 0; i < (argc - 2); i++) {
SAFE_STRCOPY(param[i], argv[i + 2]);
switch(i) {
case 0:
src_file = param[i];
break;
case 1:
packed_file = reduced_file = param[i];
break;
case 2:
packed_bin_file = list_file = param[i];
break;
}
}
if (!strncmp(argv[1], "-p", 2)) {
if (pack_rules(src_file, packed_file, packed_bin_file) != 0)
goto show_help;
return 0;
} else if (!strncmp(argv[1], "-r", 2) && list_file) {
if (reduce_rules(src_file, reduced_file, list_file) != 0)
goto show_help;
return 0;
}
printf("Invalid command\n");
show_help:
printf("Defex rules processing utility.\nUSAGE:\n%s <CMD> <PARAMS>\n"
"Commands:\n"
" -p - Pack rules file to the tree. Params: <SOURCE_FILE> <PACKED_FILE> [PACKED_BIN_FILE]\n"
" -r - Reduce rules file (remove unexistent files). Params: <SOURCE_FILE> <REDUCED_FILE> <FILE_LIST>\n"
" -s - Show rules binary file content. Params: <PACKED_BIN_FILE>\n",
argv[0]);
return -1;
}

View file

@ -1,108 +0,0 @@
# Task Integrity Verifier
config FIVE
bool "File Based Task Integrity Verifier (FIVE)(based on IMA)"
depends on INTEGRITY && DM_VERITY && BLK_DEV_LOOP
select CRYPTO
select CRYPTO_SHA1
select CRYPTO_SHA1_ARM64_CE if ARM64_CRYPTO && KERNEL_MODE_NEON
select CRYPTO_HASH_INFO
select INTEGRITY_SIGNATURE
select INTEGRITY_ASYMMETRIC_KEYS
default n
help
File Based Task Integrity Verifier (FIVE) maintains
signatures of executables and other sensitive system files,
as they are read or executed. If an attacker manages
to change the contents of an important system file
being measured, we can tell.
config FIVE_GKI_10
bool "GKI 1.0 compatible version of FIVE"
depends on FIVE
default n
help
Build GKI 1.0 compatible version of FIVE
config FIVE_GKI_20
bool "GKI 2.0 compatible version of FIVE"
depends on FIVE
default n
help
Build GKI 2.0 compatible version of FIVE
config FIVE_DEBUG
bool "FIVE Debug mode"
depends on FIVE
default n
help
Enable the debug mode in the FIVE
config FIVE_CERT_ENG
string "FIVE certificate to verify signatures for eng binary"
depends on FIVE_DEBUG
default "x509_five_eng.der"
help
Path to CERT which will be built-in to eng binary
config FIVE_CERT_USER
string "FIVE certificate to verify signatures for user binary"
depends on FIVE
default "x509_five_user.der"
help
Path to CERT which will be built-in to user binary
choice
prompt "Default integrity hash algorithm"
depends on FIVE
default FIVE_DEFAULT_HASH_SHA1
help
Select the default hash algorithm used for the measurement
list, integrity appraisal and audit log.
config FIVE_DEFAULT_HASH_SHA1
bool "SHA1 (default)"
depends on CRYPTO_SHA1
config FIVE_DEFAULT_HASH_SHA256
bool "SHA256"
depends on CRYPTO_SHA256
config FIVE_DEFAULT_HASH_SHA512
bool "SHA512"
depends on CRYPTO_SHA512
config FIVE_DEFAULT_HASH_WP512
bool "WP512"
depends on CRYPTO_WP512
endchoice
config FIVE_DEFAULT_HASH
string
depends on FIVE
default "sha1" if FIVE_DEFAULT_HASH_SHA1
default "sha256" if FIVE_DEFAULT_HASH_SHA256
default "sha512" if FIVE_DEFAULT_HASH_SHA512
default "wp512" if FIVE_DEFAULT_HASH_WP512
config FIVE_TRUSTED_KEYRING
bool "Require all keys on the .five keyring be signed"
depends on FIVE && SYSTEM_TRUSTED_KEYRING
default y
help
This option requires that all keys added to the .five
keyring be signed by a key on the system trusted keyring.
config FIVE_PA_FEATURE
bool "Process authenticator"
depends on FIVE && !PROCA
default y
help
Enable Process Authenticator related code
config FIVE_AUDIT_VERBOSE
bool "FIVE verbose audit logs"
depends on FIVE_DEBUG
default n
help
Enable verbose audit logs.

View file

@ -1,36 +0,0 @@
obj-$(CONFIG_FIVE) += five.o
obj-$(CONFIG_FIVE_PA_FEATURE) += five_pa.o
EXTRA_CFLAGS += -I$(src)
asflags-y += -Isecurity/integrity/five
asflags-y += -Isecurity/samsung/five
ccflags-y += -I$(srctree)
ccflags-y += -Wformat
five-y := five_lv.o five_cert.o five_keyring.o five_init.o \
five_cert_builtin.o five_cache.o \
five_dmverity.o
five-$(CONFIG_FIVE_DSMS) += five_dsms.o
ifdef CONFIG_FIVE_GKI_20
FILES := five_main.o five_appraise.o five_crypto.o five_audit.o \
five_hooks.o task_integrity.o five_state.o five_vfs.o \
five_tint_dev.o five_tee_interface.o
five-y += $(addprefix gki/,$(FILES))
else
five-y += five_crypto.o five_audit.o \
five_hooks.o task_integrity.o five_state.o
ifdef CONFIG_PROCA_S_OS
five-y += s_os/five_main.o s_os/five_appraise.o
else
five-y += five_main.o five_appraise.o five_tee_interface.o
endif
endif
# kunit tests options:
ifeq ($(CONFIG_SEC_KUNIT)$(CONFIG_UML), yy)
GCOV_PROFILE := y
five-$(CONFIG_FIVE) += five_dsms.o
EXTRA_CFLAGS += -DFIVE_KUNIT_ENABLED
endif

View file

@ -1,116 +0,0 @@
/*
* This code is based on IMA's code
*
* Copyright (C) 2016 Samsung Electronics, Inc.
*
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
* Viacheslav Vovchenko <v.vovchenko@samsung.com>
* Yevgen Kopylov <y.kopylov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_FIVE_H
#define __LINUX_FIVE_H
#include <linux/types.h>
#include <linux/crypto.h>
#include <linux/security.h>
#include <linux/hash.h>
#include <linux/audit.h>
#include <linux/workqueue.h>
#include <linux/xattr.h>
#include "five_cert.h"
#include "five_crypto.h"
#define XATTR_FIVE_SUFFIX "five"
#define XATTR_NAME_FIVE (XATTR_SECURITY_PREFIX XATTR_FIVE_SUFFIX)
#define XATTR_PA_SUFFIX "pa"
#define XATTR_NAME_PA (XATTR_USER_PREFIX XATTR_PA_SUFFIX)
/* set during initialization */
extern int five_hash_algo;
struct worker_context {
struct work_struct data_work;
struct task_integrity *tint;
};
struct f_signature_task {
struct task_integrity *tint;
struct file *file;
};
struct f_signature_context {
struct work_struct data_work;
struct f_signature_task payload;
};
struct five_stat {
u64 inode_iversion;
u64 cache_iversion;
u32 cache_status;
u32 is_dm_verity;
};
/* Internal FIVE function definitions */
int five_init(void);
/* FIVE policy related functions */
enum five_hooks {
FILE_CHECK = 1,
MMAP_CHECK,
BPRM_CHECK,
POST_SETATTR
};
struct file_verification_result {
struct task_struct *task;
struct file *file;
struct integrity_iint_cache *iint;
enum five_hooks fn;
int five_result;
void *xattr;
size_t xattr_len;
};
static inline void file_verification_result_init(
struct file_verification_result *result)
{
memset(result, 0, sizeof(*result));
}
static inline void file_verification_result_deinit(
struct file_verification_result *result)
{
kfree(result->xattr);
memset(result, 0, sizeof(*result));
}
int five_appraise_measurement(struct task_struct *task, int func,
struct integrity_iint_cache *iint,
struct file *file,
struct five_cert *cert);
int five_read_xattr(struct dentry *dentry, char **xattr_value);
int five_check_params(struct task_struct *task, struct file *file);
const char *five_d_path(const struct path *path, char **pathbuf,
char *namebuf);
int five_digsig_verify(struct five_cert *cert,
const char *digest, int digestlen);
int five_reboot_notifier(struct notifier_block *nb,
unsigned long action, void *unused);
int __init five_load_built_x509(void);
int __init five_keyring_init(void);
const char *five_get_string_fn(enum five_hooks fn);
#endif

View file

@ -1,852 +0,0 @@
/*
* This code is based on IMA's code
*
* Copyright (C) 2016 Samsung Electronics, Inc.
*
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
* Viacheslav Vovchenko <v.vovchenko@samsung.com>
* Yevgen Kopylov <y.kopylov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/magic.h>
#include <crypto/hash_info.h>
#include <linux/task_integrity.h>
#include "five.h"
#include "five_audit.h"
#include "five_hooks.h"
#include "five_tee_api.h"
#include "five_porting.h"
#include "five_cache.h"
#include "five_dmverity.h"
#define FIVE_RSA_SIGNATURE_MAX_LENGTH (2048/8)
/* Identify extend structure of integrity label */
#define FIVE_ID_INTEGRITY_LABEL_EX 0xFFFF
#define FIVE_LABEL_VERSION1 1
/* Maximum length of data integrity label.
* This limit is applied because:
* 1. TEEgris doesn't support signing data longer than 480 bytes;
* 2. The label's length is limited to 3965 byte according to the data
* transmission protocol between five_tee_driver and TA.
*/
#define FIVE_LABEL_MAX_LEN 256
/**
* Extend structure of integrity label.
* If field "len" equals 0xffff then it is extend integrity label,
* otherwise simple integrity label.
*/
struct integrity_label_ex {
uint16_t len;
uint8_t version;
uint8_t reserved[2];
uint8_t hash_algo;
uint8_t hash[64];
struct integrity_label label;
} __packed;
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
static const bool panic_on_error = true;
#else
static const bool panic_on_error;
#endif
static DECLARE_RWSEM(sign_fcntl_lock);
/*
* five_collect_measurement - collect file measurement
*
* Must be called with iint->mutex held.
*
* Return 0 on success, error code otherwise.
*/
static int five_collect_measurement(struct file *file, u8 hash_algo,
u8 *hash, size_t hash_len)
{
int result = 0;
BUG_ON(!file || !hash);
result = five_calc_file_hash(file, hash_algo, hash, &hash_len);
if (result) {
five_audit_err(current, file, "collect_measurement", 0,
0, "calculate file hash failed", result);
}
return result;
}
static int get_integrity_label(struct five_cert *cert,
void **label_data, size_t *label_len)
{
int rc = -ENODATA;
struct five_cert_header *header =
(struct five_cert_header *)cert->body.header->value;
if (header && header->signature_type == FIVE_XATTR_HMAC) {
*label_data = cert->body.label->value;
*label_len = cert->body.label->length;
rc = 0;
}
return rc;
}
static int get_signature(struct five_cert *cert, void **sig,
size_t *sig_len)
{
int rc = -ENODATA;
struct five_cert_header *header =
(struct five_cert_header *)cert->body.header->value;
if (header && header->signature_type == FIVE_XATTR_HMAC) {
if (cert->signature->length == 0)
return rc;
*sig = cert->signature->value;
*sig_len = cert->signature->length;
rc = 0;
}
return rc;
}
static int update_label(struct integrity_iint_cache *iint,
const void *label_data, size_t label_len)
{
struct integrity_label *l;
if (!label_data)
return 0;
l = kmalloc(sizeof(struct integrity_label) + label_len, GFP_NOFS);
if (l) {
l->len = label_len;
memcpy(l->data, label_data, label_len);
kfree(iint->five_label);
iint->five_label = l;
} else {
return -ENOMEM;
}
return 0;
}
static int five_fix_xattr(struct task_struct *task,
struct dentry *dentry,
struct file *file,
void **raw_cert,
size_t *raw_cert_len,
struct integrity_iint_cache *iint,
struct integrity_label_ex *label)
{
int rc = 0;
u8 hash[FIVE_MAX_DIGEST_SIZE], *hash_file, *sig = NULL;
size_t hash_len = sizeof(hash), hash_file_len, sig_len;
void *file_label = label->label.data;
u16 file_label_len = label->label.len;
struct five_cert_body body_cert = {0};
struct five_cert_header *header;
BUG_ON(!task || !dentry || !file || !raw_cert || !(*raw_cert) || !iint);
BUG_ON(!raw_cert_len);
rc = five_cert_body_fillout(&body_cert, *raw_cert, *raw_cert_len);
if (unlikely(rc))
return -EINVAL;
header = (struct five_cert_header *)body_cert.header->value;
hash_file = body_cert.hash->value;
hash_file_len = body_cert.hash->length;
if (unlikely(!header || !hash_file))
return -EINVAL;
if (label->version == FIVE_LABEL_VERSION1) {
rc = five_collect_measurement(file, header->hash_algo,
hash_file, hash_file_len);
if (unlikely(rc))
return rc;
} else {
memcpy(hash_file, label->hash, hash_file_len);
}
rc = five_cert_calc_hash(&body_cert, hash, &hash_len);
if (unlikely(rc))
return rc;
sig_len = (size_t)body_cert.hash->length + file_label_len;
sig = kzalloc(sig_len, GFP_NOFS);
if (!sig)
return -ENOMEM;
rc = sign_hash(header->hash_algo, hash, hash_len,
file_label, file_label_len, sig, &sig_len);
if (!rc) {
rc = five_cert_append_signature(raw_cert, raw_cert_len,
sig, sig_len);
if (!rc) {
int count = 1;
do {
rc = __vfs_setxattr_noperm(d_real_comp(dentry),
XATTR_NAME_FIVE,
*raw_cert,
*raw_cert_len,
0);
count--;
} while (count >= 0 && rc != 0);
if (!rc) {
rc = update_label(iint,
file_label, file_label_len);
}
}
} else if (panic_on_error) {
panic("FIVE failed to sign %s (ret code = %d)",
dentry->d_name.name, rc);
} else {
five_audit_sign_err(current, file, "fix_xattr", 0,
0, "can't sign the file", rc);
}
kfree(sig);
return rc;
}
int five_read_xattr(struct dentry *dentry, char **xattr_value)
{
ssize_t ret;
ret = vfs_getxattr_alloc(dentry, XATTR_NAME_FIVE, xattr_value,
0, GFP_NOFS);
if (ret < 0)
ret = 0;
return ret;
}
static bool bad_fs(struct inode *inode)
{
if (inode->i_sb->s_magic == EXT4_SUPER_MAGIC ||
inode->i_sb->s_magic == F2FS_SUPER_MAGIC ||
inode->i_sb->s_magic == OVERLAYFS_SUPER_MAGIC ||
inode->i_sb->s_magic == EROFS_SUPER_MAGIC_V1)
return false;
return true;
}
static bool readonly_sb(struct inode *inode)
{
if (inode->i_sb->s_flags & MS_RDONLY)
return true;
return false;
}
/*
* five_is_fsverity_protected - checks if file is protected by FSVERITY
*
* Return true/false
*/
static bool five_is_fsverity_protected(const struct inode *inode)
{
return IS_VERITY(inode);
}
/*
* five_appraise_measurement - appraise file measurement
*
* Return 0 on success, error code otherwise
*/
int five_appraise_measurement(struct task_struct *task, int func,
struct integrity_iint_cache *iint,
struct file *file,
struct five_cert *cert)
{
enum task_integrity_reset_cause cause = CAUSE_UNKNOWN;
struct dentry *dentry = NULL;
struct inode *inode = NULL;
enum five_file_integrity status = FIVE_FILE_UNKNOWN;
enum task_integrity_value prev_integrity;
int rc = 0;
u8 *file_hash;
u8 stored_file_hash[FIVE_MAX_DIGEST_SIZE] = {0};
size_t file_hash_len = 0;
struct five_cert_header *header = NULL;
BUG_ON(!task || !iint || !file);
prev_integrity = task_integrity_read(TASK_INTEGRITY(task));
dentry = file->f_path.dentry;
inode = d_backing_inode(dentry);
if (bad_fs(inode)) {
status = FIVE_FILE_FAIL;
cause = CAUSE_BAD_FS;
rc = -ENOTSUPP;
goto out;
}
if (!cert) {
cause = CAUSE_NO_CERT;
if (five_is_fsverity_protected(inode))
status = FIVE_FILE_FSVERITY;
else if (five_is_dmverity_protected(file))
status = FIVE_FILE_DMVERITY;
goto out;
}
header = (struct five_cert_header *)cert->body.header->value;
file_hash = cert->body.hash->value;
file_hash_len = cert->body.hash->length;
if (file_hash_len > sizeof(stored_file_hash)) {
cause = CAUSE_INVALID_HASH_LENGTH;
rc = -EINVAL;
goto out;
}
memcpy(stored_file_hash, file_hash, file_hash_len);
if (unlikely(!header || !file_hash)) {
cause = CAUSE_INVALID_HEADER;
rc = -EINVAL;
goto out;
}
rc = five_collect_measurement(file, header->hash_algo, file_hash,
file_hash_len);
if (rc) {
cause = CAUSE_CALC_HASH_FAILED;
goto out;
}
switch (header->signature_type) {
case FIVE_XATTR_HMAC: {
u8 *sig = NULL;
u8 algo = header->hash_algo;
void *file_label_data;
size_t file_label_len, sig_len = 0;
u8 cert_hash[FIVE_MAX_DIGEST_SIZE] = {0};
size_t cert_hash_len = sizeof(cert_hash);
status = FIVE_FILE_FAIL;
rc = get_integrity_label(cert, &file_label_data,
&file_label_len);
if (unlikely(rc)) {
cause = CAUSE_INVALID_LABEL_DATA;
break;
}
if (unlikely(file_label_len > PAGE_SIZE)) {
cause = CAUSE_INVALID_LABEL_DATA;
break;
}
rc = get_signature(cert, (void **)&sig, &sig_len);
if (unlikely(rc)) {
cause = CAUSE_INVALID_SIGNATURE_DATA;
break;
}
rc = five_cert_calc_hash(&cert->body, cert_hash,
&cert_hash_len);
if (unlikely(rc)) {
cause = CAUSE_INVALID_CALC_CERT_HASH;
break;
}
rc = verify_hash(algo, cert_hash,
cert_hash_len,
file_label_data, file_label_len,
sig, sig_len);
if (unlikely(rc)) {
cause = CAUSE_INVALID_HASH;
if (cert) {
five_audit_hexinfo(file, "stored hash",
stored_file_hash, file_hash_len);
five_audit_hexinfo(file, "calculated hash",
file_hash, file_hash_len);
five_audit_hexinfo(file, "HMAC signature",
sig, sig_len);
}
break;
}
rc = update_label(iint, file_label_data, file_label_len);
if (unlikely(rc)) {
cause = CAUSE_INVALID_UPDATE_LABEL;
break;
}
status = FIVE_FILE_HMAC;
break;
}
case FIVE_XATTR_DIGSIG: {
u8 cert_hash[FIVE_MAX_DIGEST_SIZE] = {0};
size_t cert_hash_len = sizeof(cert_hash);
status = FIVE_FILE_FAIL;
rc = five_cert_calc_hash(&cert->body, cert_hash,
&cert_hash_len);
if (unlikely(rc)) {
cause = CAUSE_INVALID_CALC_CERT_HASH;
break;
}
rc = five_digsig_verify(cert, cert_hash, cert_hash_len);
if (rc) {
cause = CAUSE_INVALID_SIGNATURE;
if (cert) {
five_audit_hexinfo(file, "stored hash",
stored_file_hash, file_hash_len);
five_audit_hexinfo(file, "calculated hash",
file_hash, file_hash_len);
five_audit_hexinfo(file, "RSA signature",
cert->signature->value,
cert->signature->length);
}
break;
}
status = FIVE_FILE_RSA;
break;
}
default:
status = FIVE_FILE_FAIL;
cause = CAUSE_UKNOWN_FIVE_DATA;
break;
}
out:
if (status == FIVE_FILE_FAIL || status == FIVE_FILE_UNKNOWN) {
task_integrity_set_reset_reason(TASK_INTEGRITY(task),
cause, file);
five_audit_verbose(task, file, five_get_string_fn(func),
prev_integrity, prev_integrity,
tint_reset_cause_to_string(cause), rc);
}
five_set_cache_status(iint, status);
return rc;
}
/*
* five_update_xattr - update 'security.five' hash value
*/
static int five_update_xattr(struct task_struct *task,
struct integrity_iint_cache *iint, struct file *file,
struct integrity_label_ex *label)
{
struct dentry *dentry;
int rc = 0;
uint8_t *hash;
size_t hash_len;
uint8_t *raw_cert;
size_t raw_cert_len;
struct five_cert_header header = {
.version = FIVE_CERT_VERSION1,
.privilege = FIVE_PRIV_DEFAULT,
.hash_algo = five_hash_algo,
.signature_type = FIVE_XATTR_HMAC };
BUG_ON(!task || !iint || !file || !label);
if (label->version == FIVE_LABEL_VERSION1) {
hash_len = (size_t)hash_digest_size[five_hash_algo];
} else {
header.hash_algo = label->hash_algo;
if (label->hash_algo >= HASH_ALGO__LAST)
return -EINVAL;
hash_len = (size_t)hash_digest_size[label->hash_algo];
if (hash_len > sizeof(label->hash))
return -EINVAL;
}
hash = kzalloc(hash_len, GFP_KERNEL);
if (!hash)
return -ENOMEM;
dentry = file->f_path.dentry;
/* do not collect and update hash for digital signatures */
if (five_get_cache_status(iint) == FIVE_FILE_RSA) {
char dummy[512];
struct inode *inode = file_inode(file);
rc = __vfs_getxattr(d_real_comp(dentry), inode, XATTR_NAME_FIVE,
dummy, sizeof(dummy), XATTR_NOSECURITY);
// Check if xattr is exist
if (rc > 0 || rc != -ENODATA) {
kfree(hash);
return -EPERM;
} else { // xattr does not exist.
five_set_cache_status(iint, FIVE_FILE_UNKNOWN);
pr_err("FIVE: ERROR: Cache is unsynchronized");
}
}
rc = five_cert_body_alloc(&header, hash, hash_len,
label->label.data, label->label.len,
&raw_cert, &raw_cert_len);
if (rc)
goto exit;
if (task_integrity_allow_sign(TASK_INTEGRITY(task))) {
rc = five_fix_xattr(task, dentry, file,
(void **)&raw_cert, &raw_cert_len, iint, label);
if (rc)
pr_err("FIVE: Can't sign hash: rc=%d\n", rc);
} else {
rc = -EPERM;
}
five_hook_file_signed(task, file, raw_cert, raw_cert_len, rc);
five_cert_free(raw_cert);
exit:
kfree(hash);
return rc;
}
static void five_reset_appraise_flags(struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
struct integrity_iint_cache *iint;
if (!S_ISREG(inode->i_mode))
return;
iint = integrity_iint_find(inode);
if (iint)
five_set_cache_status(iint, FIVE_FILE_UNKNOWN);
}
/**
* five_inode_post_setattr - reflect file metadata changes
* @dentry: pointer to the affected dentry
*
* Changes to a dentry's metadata might result in needing to appraise.
*
* This function is called from notify_change(), which expects the caller
* to lock the inode's i_mutex.
*/
void five_inode_post_setattr(struct task_struct *task, struct dentry *dentry)
{
five_reset_appraise_flags(dentry);
}
/*
* five_protect_xattr - protect 'security.five'
*
* Ensure that not just anyone can modify or remove 'security.five'.
*/
static int five_protect_xattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
if (strcmp(xattr_name, XATTR_NAME_FIVE) == 0) {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
return 1;
}
return 0;
}
int five_inode_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
int result = five_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len);
if (result == 1 && xattr_value_len == 0) {
five_reset_appraise_flags(dentry);
return 0;
}
if (result == 1) {
bool digsig;
struct five_cert_header *header;
struct five_cert cert = { {0} };
result = five_cert_fillout(&cert, xattr_value, xattr_value_len);
if (result)
return result;
header = (struct five_cert_header *)cert.body.header->value;
if (!xattr_value_len || !header ||
(header->signature_type >= FIVE_XATTR_END))
return -EINVAL;
digsig = (header->signature_type == FIVE_XATTR_DIGSIG);
if (!digsig)
return -EPERM;
five_reset_appraise_flags(dentry);
result = 0;
}
return result;
}
int five_inode_removexattr(struct dentry *dentry, const char *xattr_name)
{
int result;
result = five_protect_xattr(dentry, xattr_name, NULL, 0);
if (result == 1) {
five_reset_appraise_flags(dentry);
result = 0;
}
return result;
}
int five_reboot_notifier(struct notifier_block *nb,
unsigned long action, void *unused)
{
down_write(&sign_fcntl_lock);
/* Need to wait for five_fcntl_sign finish */
up_write(&sign_fcntl_lock);
return NOTIFY_DONE;
}
static int copy_label(const struct integrity_label __user *ulabel,
struct integrity_label_ex **out_label)
{
u16 len;
size_t label_len;
int rc = 0;
struct integrity_label_ex header = {0};
struct integrity_label_ex *label = NULL;
if (unlikely(!ulabel || !out_label)) {
rc = -EINVAL;
goto error;
}
if (unlikely(copy_from_user(&len, ulabel, sizeof(len)))) {
rc = -EFAULT;
goto error;
}
if (len == FIVE_ID_INTEGRITY_LABEL_EX) {
if (unlikely(copy_from_user(&header, ulabel, sizeof(header)))) {
rc = -EFAULT;
goto error;
}
if (len != header.len ||
header.label.len > FIVE_LABEL_MAX_LEN ||
header.version <= FIVE_LABEL_VERSION1) {
rc = -EINVAL;
goto error;
}
label_len = sizeof(header) + header.label.len;
label = kzalloc(label_len, GFP_NOFS);
if (unlikely(!label)) {
rc = -ENOMEM;
goto error;
}
memcpy(label, &header, sizeof(header));
if (unlikely(copy_from_user(&label->label.data[0],
(const u8 __user *)ulabel + sizeof(header),
label_len - sizeof(header)))) {
rc = -EFAULT;
goto error;
}
} else {
if (len > FIVE_LABEL_MAX_LEN) {
rc = -EINVAL;
goto error;
}
label_len = sizeof(header) + len;
label = kzalloc(label_len, GFP_NOFS);
if (unlikely(!label)) {
rc = -ENOMEM;
goto error;
}
if (unlikely(copy_from_user(&label->label, ulabel,
sizeof(len) + len))) {
rc = -EFAULT;
goto error;
}
if (len != label->label.len) {
rc = -EINVAL;
goto error;
}
label->version = FIVE_LABEL_VERSION1;
}
*out_label = label;
error:
if (rc)
kfree(label);
return rc;
}
/* Called from do_fcntl */
int five_fcntl_sign(struct file *file, struct integrity_label __user *label)
{
struct integrity_iint_cache *iint;
struct inode *inode = file_inode(file);
struct integrity_label_ex *l = NULL;
int rc = 0;
if (!S_ISREG(inode->i_mode))
return -EINVAL;
if (readonly_sb(inode)) {
pr_err("FIVE: Can't sign a file on RO FS\n");
return -EROFS;
}
if (task_integrity_allow_sign(TASK_INTEGRITY(current))) {
rc = copy_label(label, &l);
if (rc) {
pr_err("FIVE: Can't copy integrity label\n");
return rc;
}
} else {
enum task_integrity_value tint =
task_integrity_read(TASK_INTEGRITY(current));
five_audit_err(current, file, "fcntl_sign", tint, tint,
"sign:no-perm", -EPERM);
return -EPERM;
}
iint = integrity_inode_get(inode);
if (!iint) {
kfree(l);
return -ENOMEM;
}
if (file->f_op && file->f_op->flush) {
if (file->f_op->flush(file, current->files)) {
kfree(l);
return -EOPNOTSUPP;
}
}
down_read(&sign_fcntl_lock);
inode_lock(inode);
rc = five_update_xattr(current, iint, file, l);
iint->five_signing = false;
inode_unlock(inode);
up_read(&sign_fcntl_lock);
kfree(l);
return rc;
}
static int check_input_inode(struct inode *inode)
{
if (!S_ISREG(inode->i_mode))
return -EINVAL;
if (readonly_sb(inode)) {
pr_err("FIVE: Can't sign a file on RO FS\n");
return -EROFS;
}
return 0;
}
int five_fcntl_edit(struct file *file)
{
int rc;
struct dentry *dentry;
uint8_t *raw_cert = NULL;
size_t raw_cert_len = 0;
struct integrity_iint_cache *iint;
struct inode *inode = file_inode(file);
rc = check_input_inode(inode);
if (rc)
return rc;
if (!task_integrity_allow_sign(TASK_INTEGRITY(current)))
return -EPERM;
inode_lock(inode);
dentry = file->f_path.dentry;
rc = __vfs_setxattr_noperm(d_real_comp(dentry),
XATTR_NAME_FIVE,
raw_cert,
raw_cert_len,
0);
iint = integrity_inode_get(inode);
if (iint)
iint->five_signing = true;
inode_unlock(inode);
return rc;
}
int five_fcntl_close(struct file *file)
{
int rc;
ssize_t xattr_len;
struct dentry *dentry;
struct integrity_iint_cache *iint;
struct inode *inode = file_inode(file);
rc = check_input_inode(inode);
if (rc)
return rc;
inode_lock(inode);
iint = integrity_inode_get(inode);
if (!iint) {
inode_unlock(inode);
return -ENOMEM;
}
if (iint->five_signing) {
dentry = file->f_path.dentry;
xattr_len = __vfs_getxattr(d_real_comp(dentry), inode,
XATTR_NAME_FIVE, NULL, 0, XATTR_NOSECURITY);
if (xattr_len == 0)
rc = __vfs_removexattr(d_real_comp(dentry),
XATTR_NAME_FIVE);
iint->five_signing = false;
}
inode_unlock(inode);
return rc;
}

View file

@ -1,252 +0,0 @@
/*
* Audit calls for FIVE audit subsystem.
*
* Copyright (C) 2017 Samsung Electronics, Inc.
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/fs.h>
#include <linux/gfp.h>
#include <linux/audit.h>
#include <linux/task_integrity.h>
#include <linux/version.h>
#include "five.h"
#include "five_audit.h"
#include "five_cache.h"
#include "five_porting.h"
#include "five_dsms.h"
#include "five_testing.h"
__visible_for_testing __mockable
void five_audit_msg(struct task_struct *task, struct file *file,
const char *op, enum task_integrity_value prev,
enum task_integrity_value tint, const char *cause, int result);
#ifdef CONFIG_FIVE_AUDIT_VERBOSE
__mockable
void five_audit_verbose(struct task_struct *task, struct file *file,
const char *op, enum task_integrity_value prev,
enum task_integrity_value tint, const char *cause, int result)
{
five_audit_msg(task, file, op, prev, tint, cause, result);
}
#else
__mockable
void five_audit_verbose(struct task_struct *task, struct file *file,
const char *op, enum task_integrity_value prev,
enum task_integrity_value tint, const char *cause, int result)
{
}
#endif
void five_audit_info(struct task_struct *task, struct file *file,
const char *op, enum task_integrity_value prev,
enum task_integrity_value tint, const char *cause, int result)
{
five_audit_msg(task, file, op, prev, tint, cause, result);
}
__visible_for_testing __mockable
void call_five_dsms_reset_integrity(const char *task_name, int result,
const char *file_name)
{
five_dsms_reset_integrity(task_name, result, file_name);
}
/**
* There are two kind of event that can come to the function: error
* and tampering attempt. 'result' is for identification of error type
* and it should be non-zero in case of error but is always zero in
* case of tampering.
*/
void five_audit_err(struct task_struct *task, struct file *file,
const char *op, enum task_integrity_value prev,
enum task_integrity_value tint, const char *cause, int result)
{
five_audit_msg(task, file, op, prev, tint, cause, result);
if (!result) {
char comm[TASK_COMM_LEN];
struct task_struct *tsk = task ? task : current;
call_five_dsms_reset_integrity(get_task_comm(comm, tsk), 0, op);
}
}
__visible_for_testing __mockable
void call_five_dsms_sign_err(const char *app, int result)
{
five_dsms_sign_err(app, result);
}
void five_audit_sign_err(struct task_struct *task, struct file *file,
const char *op, enum task_integrity_value prev,
enum task_integrity_value tint, const char *cause, int result)
{
char comm[TASK_COMM_LEN];
struct task_struct *tsk = task ? task : current;
get_task_comm(comm, tsk);
call_five_dsms_sign_err(comm, result);
}
__visible_for_testing __mockable
void five_audit_msg(struct task_struct *task, struct file *file,
const char *op, enum task_integrity_value prev,
enum task_integrity_value tint, const char *cause, int result)
{
struct audit_buffer *ab;
struct inode *inode = NULL;
const char *fname = NULL;
char *pathbuf = NULL;
char filename[NAME_MAX];
char comm[TASK_COMM_LEN];
const char *name = "";
struct task_struct *tsk = task ? task : current;
struct integrity_iint_cache *iint = NULL;
if (file) {
inode = file_inode(file);
fname = five_d_path(&file->f_path, &pathbuf, filename);
}
ab = audit_log_start(current->audit_context, GFP_KERNEL,
AUDIT_INTEGRITY_DATA);
if (unlikely(!ab)) {
pr_err("Can't get a context of audit logs\n");
goto exit;
}
audit_log_format(ab, " pid=%d", task_pid_nr(tsk));
audit_log_format(ab, " tgid=%d", task_tgid_nr(tsk));
audit_log_task_context(ab);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
audit_log_format(ab, " op=%s", op);
#else
audit_log_format(ab, " op=");
audit_log_string(ab, op);
#endif
audit_log_format(ab, " cint=0x%x", tint);
audit_log_format(ab, " pint=0x%x", prev);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
audit_log_format(ab, " cause=%s", cause);
#else
audit_log_format(ab, " cause=");
audit_log_string(ab, cause);
#endif
audit_log_format(ab, " comm=");
audit_log_untrustedstring(ab, get_task_comm(comm, tsk));
if (fname) {
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, fname);
name = fname;
}
if (inode) {
audit_log_format(ab, " dev=");
audit_log_untrustedstring(ab, inode->i_sb->s_id);
audit_log_format(ab, " ino=%lu", inode->i_ino);
audit_log_format(ab, " i_version=%llu ",
inode_query_iversion(inode));
iint = integrity_inode_get(inode);
if (iint) {
audit_log_format(ab, " five_status=%d ",
five_get_cache_status(iint));
audit_log_format(ab, " version=%llu ",
(unsigned long long)iint->version);
}
}
audit_log_format(ab, " res=%d", result);
audit_log_end(ab);
exit:
if (pathbuf)
__putname(pathbuf);
}
void five_audit_tee_msg(const char *func, const char *cause, int rc,
uint32_t origin)
{
struct audit_buffer *ab;
ab = audit_log_start(current->audit_context, GFP_KERNEL,
AUDIT_INTEGRITY_DATA);
if (unlikely(!ab)) {
pr_err("Can't get a context of audit logs\n");
return;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
audit_log_format(ab, " func=%s", func);
audit_log_format(ab, " cause=%s", cause);
#else
audit_log_format(ab, " func=");
audit_log_string(ab, func);
audit_log_format(ab, " cause=");
audit_log_string(ab, cause);
#endif
audit_log_format(ab, " rc=0x%x, ", rc);
audit_log_format(ab, " origin=0x%x", origin);
audit_log_end(ab);
}
void five_audit_hexinfo(struct file *file, const char *msg, char *data,
size_t data_length)
{
struct audit_buffer *ab;
struct inode *inode = NULL;
const unsigned char *fname = NULL;
char filename[NAME_MAX];
char *pathbuf = NULL;
struct integrity_iint_cache *iint = NULL;
if (file) {
fname = five_d_path(&file->f_path, &pathbuf, filename);
inode = file_inode(file);
}
ab = audit_log_start(current->audit_context, GFP_KERNEL,
AUDIT_INTEGRITY_DATA);
if (unlikely(!ab)) {
pr_err("Can't get a context of audit logs\n");
goto exit;
}
if (fname) {
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, fname);
}
if (inode) {
audit_log_format(ab, " i_version=%llu ",
inode_query_iversion(inode));
iint = integrity_inode_get(inode);
if (iint) {
audit_log_format(ab, " cache_value=%lu ",
iint->five_flags);
audit_log_format(ab, " five_status=%d ",
five_get_cache_status(iint));
audit_log_format(ab, " version=%llu ",
(unsigned long long)iint->version);
}
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
audit_log_format(ab, "%s", msg);
#else
audit_log_string(ab, msg);
#endif
audit_log_n_hex(ab, data, data_length);
audit_log_end(ab);
exit:
if (pathbuf)
__putname(pathbuf);
}

View file

@ -1,40 +0,0 @@
/*
* Audit calls for FIVE audit subsystem.
*
* Copyright (C) 2017 Samsung Electronics, Inc.
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_FIVE_AUDIT_H
#define __LINUX_FIVE_AUDIT_H
#include <linux/task_integrity.h>
void five_audit_verbose(struct task_struct *task, struct file *file,
const char *op, enum task_integrity_value prev,
enum task_integrity_value tint, const char *cause, int result);
void five_audit_info(struct task_struct *task, struct file *file,
const char *op, enum task_integrity_value prev,
enum task_integrity_value tint, const char *cause, int result);
void five_audit_err(struct task_struct *task, struct file *file,
const char *op, enum task_integrity_value prev,
enum task_integrity_value tint, const char *cause, int result);
void five_audit_sign_err(struct task_struct *task, struct file *file,
const char *op, enum task_integrity_value prev,
enum task_integrity_value tint, const char *cause, int result);
void five_audit_tee_msg(const char *func, const char *cause, int rc,
uint32_t origin);
void five_audit_hexinfo(struct file *file,
const char *msg, char *data, size_t data_length);
#endif

View file

@ -1,44 +0,0 @@
/*
* FIVE cache functions
*
* Copyright (C) 2016 Samsung Electronics, Inc.
*
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
* Viacheslav Vovchenko <v.vovchenko@samsung.com>
* Yevgen Kopylov <y.kopylov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "five_cache.h"
#include "five_porting.h"
enum five_file_integrity five_get_cache_status(
const struct integrity_iint_cache *iint)
{
if (unlikely(!iint))
return FIVE_FILE_UNKNOWN;
if (!inode_eq_iversion(iint->inode, iint->version))
return FIVE_FILE_UNKNOWN;
return iint->five_status;
}
void five_set_cache_status(struct integrity_iint_cache *iint,
enum five_file_integrity status)
{
if (unlikely(!iint))
return;
iint->version = inode_query_iversion(iint->inode);
iint->five_status = status;
}

View file

@ -1,30 +0,0 @@
/*
* FIVE cache functions
*
* Copyright (C) 2016 Samsung Electronics, Inc.
*
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
* Viacheslav Vovchenko <v.vovchenko@samsung.com>
* Yevgen Kopylov <y.kopylov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_FIVE_CACHE_H
#define __LINUX_FIVE_CACHE_H
#include "security/integrity/integrity.h"
enum five_file_integrity five_get_cache_status(
const struct integrity_iint_cache *iint);
void five_set_cache_status(struct integrity_iint_cache *iint,
enum five_file_integrity status);
#endif // __LINUX_FIVE_CACHE_H

View file

@ -1,216 +0,0 @@
/*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This code is originated from Samsung Electronics proprietary sources.
* Author: Viacheslav Vovchenko, <v.vovchenko@samsung.com>
* Created: 10 Jul 2017
*
* Copyright (C) 2016 Samsung Electronics, Inc.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include "five_crypto.h"
#include "five_cert.h"
int five_cert_body_alloc(struct five_cert_header *header,
uint8_t *hash, size_t hash_len,
uint8_t *label, size_t label_len,
uint8_t **raw_cert, size_t *raw_cert_len)
{
size_t data_len;
uint8_t *data;
struct lv *next;
const void *end;
int rc = 0;
struct five_cert_body body_cert = {0};
if (unlikely(!header || !raw_cert || !raw_cert_len))
return -EINVAL;
data_len = sizeof(*body_cert.header) + sizeof(*header) +
sizeof(*body_cert.hash) + hash_len +
sizeof(*body_cert.label) + label_len;
if (unlikely(data_len > FIVE_MAX_CERTIFICATE_SIZE))
return -EINVAL;
data = kzalloc(data_len, GFP_NOFS);
if (unlikely(!data))
return -ENOMEM;
next = body_cert.header = (struct lv *)data;
end = data + data_len;
/* Fill header data */
rc = lv_set(next, header, sizeof(*header), end);
if (unlikely(rc))
goto exit;
next = body_cert.hash = lv_get_next(next, end);
if (unlikely(!next))
goto exit;
/* Fill hash data */
rc = lv_set(next, hash, hash_len, end);
if (unlikely(rc))
goto exit;
next = body_cert.label = lv_get_next(next, end);
if (unlikely(!next))
goto exit;
/* Fill label data */
rc = lv_set(next, label, label_len, end);
if (unlikely(rc))
goto exit;
exit:
if (likely(!rc)) {
*raw_cert = data;
*raw_cert_len = data_len;
} else {
kfree(data);
}
return rc;
}
void five_cert_free(void *raw_cert)
{
kfree(raw_cert);
}
int five_cert_append_signature(void **raw_cert, size_t *raw_cert_len,
void *signature, size_t signature_len)
{
int rc = 0;
void *new_raw_cert;
size_t new_raw_cert_len;
const void *end;
struct five_cert cert = { {0} };
if (unlikely(!raw_cert || !raw_cert_len || !signature))
return -EINVAL;
new_raw_cert_len = *raw_cert_len + sizeof(cert.signature->length) +
signature_len;
if (unlikely(new_raw_cert_len > FIVE_MAX_CERTIFICATE_SIZE))
return -EINVAL;
new_raw_cert = krealloc(*raw_cert, new_raw_cert_len, GFP_NOFS);
if (unlikely(!new_raw_cert))
return -ENOMEM;
*raw_cert = new_raw_cert;
*raw_cert_len = new_raw_cert_len;
end = (uint8_t *)new_raw_cert + new_raw_cert_len;
rc = five_cert_fillout(&cert, new_raw_cert, new_raw_cert_len);
if (unlikely(rc))
return rc;
rc = lv_set(cert.signature, signature, signature_len, end);
return rc;
}
int five_cert_body_fillout(struct five_cert_body *body_cert,
const void *raw_cert, size_t raw_cert_len)
{
struct lv *next;
const void *end;
struct five_cert_header *hdr;
if (unlikely(!body_cert || !raw_cert ||
raw_cert_len > FIVE_MAX_CERTIFICATE_SIZE))
return -EINVAL;
next = body_cert->header = (struct lv *)raw_cert;
end = (uint8_t *)raw_cert + raw_cert_len;
/* Check if we had an error at the previous step */
if (!lv_get_next(next, end))
return -EINVAL;
if (sizeof(*hdr) != body_cert->header->length)
return -EINVAL;
hdr = (struct five_cert_header *)body_cert->header->value;
if (hdr->version != FIVE_CERT_VERSION1)
return -EINVAL;
next = body_cert->hash = lv_get_next(next, end);
if (unlikely(!next))
return -EINVAL;
next = body_cert->label = lv_get_next(next, end);
if (unlikely(!next))
return -EINVAL;
return 0;
}
int five_cert_fillout(struct five_cert *cert, const void *raw_cert,
size_t raw_cert_len)
{
int rc;
struct lv *next;
const void *end;
if (unlikely(!cert || !raw_cert))
return -EINVAL;
if (unlikely(raw_cert_len > FIVE_MAX_CERTIFICATE_SIZE))
return -EINVAL;
rc = five_cert_body_fillout(&cert->body, raw_cert, raw_cert_len);
if (unlikely(rc))
return rc;
next = cert->body.label;
end = (uint8_t *)raw_cert + raw_cert_len;
if (!lv_get_next(next, end))
return -EINVAL;
next = cert->signature = lv_get_next(next, end);
if (unlikely(!next))
return -EINVAL;
return 0;
}
int five_cert_calc_hash(struct five_cert_body *body_cert, uint8_t *out_hash,
size_t *out_hash_len)
{
int rc;
size_t hash_len, body_len;
struct five_cert_header *header;
if (unlikely(!body_cert || !out_hash || !out_hash_len))
return -EINVAL;
hash_len = *out_hash_len;
header = (struct five_cert_header *)body_cert->header->value;
body_len = sizeof(*body_cert->header) + body_cert->header->length +
sizeof(*body_cert->hash) + body_cert->hash->length +
sizeof(*body_cert->label) + body_cert->label->length;
if (unlikely(body_len > FIVE_MAX_CERTIFICATE_SIZE))
return -EINVAL;
rc = five_calc_data_hash((const uint8_t *)body_cert->header, body_len,
header->hash_algo, out_hash, &hash_len);
if (unlikely(rc))
return rc;
*out_hash_len = hash_len;
return 0;
}

View file

@ -1,124 +0,0 @@
/*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This code is originated from Samsung Electronics proprietary sources.
* Author: Viacheslav Vovchenko, <v.vovchenko@samsung.com>
* Created: 10 Jul 2017
*
* Copyright (C) 2016 Samsung Electronics, Inc.
*/
#ifndef __FIVE_CERT_H__
#define __FIVE_CERT_H__
#include "five_lv.h"
#define FIVE_MAX_DIGEST_SIZE 64
#define FIVE_MAX_CERTIFICATE_SIZE 4096
enum five_signature_type {
FIVE_XATTR_NONE = 0x00,
FIVE_XATTR_DIGSIG = 0x01,
FIVE_XATTR_HMAC = 0x02,
FIVE_XATTR_END
};
enum five_version {
FIVE_CERT_VERSION1 = 1
};
enum five_privilege {
FIVE_PRIV_DEFAULT = 0,
FIVE_PRIV_ALLOW_SIGN
};
struct five_cert_header {
uint8_t version; /* signature format version */
uint8_t signature_type; /* signature type, e.g. HMAC, RSA */
uint8_t privilege; /* privilege of file saved in certificate */
uint8_t hash_algo; /* use hash algorithm for file and signature */
uint32_t key_id; /* use for find key in keyrings */
} __packed;
/*
* ---------------------------------------------
* | Raw data of certificate FIVE in memory |
* ---------------------------------------------
* | u16 | header_len |
* | struct five_cert_header | header_value |
* | u16 | hash_len |
* | array | hash_value |
* | u16 | label_len |
* | array | label_value |
* | u16 | signature_len |
* | array | signature_value |
* ---------------------------------------------
*/
struct five_cert {
struct five_cert_body {
struct lv *header;
struct lv *hash;
struct lv *label;
} body;
struct lv *signature;
};
/**
* Allocates and fills raw certificate buffer without signature.
* Call five_cert_free() to release the allocated buffer.
* Returns 0 on success, otherwise - error.
*/
int five_cert_body_alloc(struct five_cert_header *header,
uint8_t *hash, size_t hash_len,
uint8_t *label, size_t label_len,
uint8_t **raw_cert, size_t *raw_cert_len);
/**
* Releases raw certificate buffer allocated previously by five_cert_alloc().
*/
void five_cert_free(void *raw_cert);
/**
* Appends signature to raw certificate buffer. raw_cert memory will be
* re-allocated to contain the signature.
* Returns 0 on success, otherwise - error
*/
int five_cert_append_signature(void **raw_cert, size_t *raw_cert_len,
void *signature, size_t signature_len);
/**
* Parse certificate raw data and fill items helper body_cert structure.
* Notice, body_cert object is reference to data of raw_cert. It will valid
* until raw_cert memory is valid.
* In case of raw_cert is changed we should be update body_cert again.
* Returns 0 on success, otherwise - error.
*/
int five_cert_body_fillout(struct five_cert_body *body_cert,
const void *raw_cert, size_t raw_cert_len);
/**
* Parse certificate raw data and fill items helper cert structure.
* Notice, cert object is reference to data of raw_cert. It will valid until
* raw_cert memory is valid.
* In case of raw_cert is changed we should be update cert again.
* Returns 0 on success, otherwise - error.
*/
int five_cert_fillout(struct five_cert *cert, const void *raw_cert,
size_t raw_cert_len);
/**
* Calculates hash of certificate, that contains header structure,
* hash and label.
* Returns 0 on success, otherwise - error.
*/
int five_cert_calc_hash(struct five_cert_body *body_cert, uint8_t *out_hash,
size_t *out_hash_len);
#endif /* __FIVE_CERT_H__ */

View file

@ -1,43 +0,0 @@
/*
* five_cert_builtin.S
*
* Copyright (C) 2016 Samsung Electronics, Inc.
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/export.h>
#include <linux/init.h>
#include <linux/version.h>
__INITRODATA
.align 8
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
#define VMLINUX_SYMBOL(name) name
#endif
#define GLOBAL(name) \
.globl VMLINUX_SYMBOL(name); \
VMLINUX_SYMBOL(name):
#ifdef CONFIG_FIVE_CERT_ENG
GLOBAL(five_local_ca_start_eng)
.incbin CONFIG_FIVE_CERT_ENG
GLOBAL(five_local_ca_end_eng)
#endif
#ifdef CONFIG_FIVE_CERT_USER
GLOBAL(five_local_ca_start_user)
.incbin CONFIG_FIVE_CERT_USER
GLOBAL(five_local_ca_end_user)
#endif

View file

@ -1,545 +0,0 @@
/*
* This code is based on IMA's code
*
* Copyright (C) 2016 Samsung Electronics, Inc.
*
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
* Viacheslav Vovchenko <v.vovchenko@samsung.com>
* Yevgen Kopylov <y.kopylov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/ratelimit.h>
#include <linux/file.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <crypto/hash.h>
#include <crypto/hash_info.h>
#include <linux/freezer.h>
#include "five.h"
#include "five_crypto_comp.h"
#include "five_porting.h"
#include "security/integrity/integrity.h"
#include "five_testing.h"
struct ahash_completion {
struct completion completion;
int err;
};
/* minimum file size for ahash use */
static unsigned long five_ahash_minsize;
module_param_named(ahash_minsize, five_ahash_minsize, ulong, 0644);
MODULE_PARM_DESC(ahash_minsize, "Minimum file size for ahash use");
/* default is 0 - 1 page. */
static int five_maxorder;
static unsigned long five_bufsize = PAGE_SIZE;
__visible_for_testing __mockable struct crypto_shash *call_crypto_alloc_shash(
const char *alg_name, u32 type, u32 mask)
{
return crypto_alloc_shash(alg_name, type, mask);
}
static int param_set_bufsize(const char *val, const struct kernel_param *kp)
{
unsigned long long size;
int order;
size = memparse(val, NULL);
order = get_order(size);
if (order >= MAX_ORDER)
return -EINVAL;
five_maxorder = order;
five_bufsize = PAGE_SIZE << order;
return 0;
}
static const struct kernel_param_ops param_ops_bufsize = {
.set = param_set_bufsize,
.get = param_get_uint,
};
#define param_check_bufsize(name, p) __param_check(name, p, unsigned int)
module_param_named(ahash_bufsize, five_bufsize, ulong, 0644);
MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size");
static struct crypto_shash *five_shash_tfm;
static struct crypto_ahash *five_ahash_tfm;
int __init five_init_crypto(void)
{
long rc;
five_shash_tfm = call_crypto_alloc_shash(
hash_algo_name[five_hash_algo], 0, 0);
if (IS_ERR(five_shash_tfm)) {
rc = PTR_ERR(five_shash_tfm);
pr_err("Can not allocate %s (reason: %ld)\n",
hash_algo_name[five_hash_algo], rc);
return rc;
}
return 0;
}
static struct crypto_shash *five_alloc_tfm(enum hash_algo algo)
{
struct crypto_shash *tfm = five_shash_tfm;
int rc;
if (algo < 0 || algo >= HASH_ALGO__LAST)
algo = five_hash_algo;
if (algo != five_hash_algo) {
tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
if (IS_ERR(tfm)) {
rc = PTR_ERR(tfm);
pr_err("Can not allocate %s (reason: %d)\n",
hash_algo_name[algo], rc);
}
}
return tfm;
}
static void five_free_tfm(struct crypto_shash *tfm)
{
if (tfm != five_shash_tfm)
crypto_free_shash(tfm);
}
/**
* five_alloc_pages() - Allocate contiguous pages.
* @max_size: Maximum amount of memory to allocate.
* @allocated_size: Returned size of actual allocation.
* @last_warn: Should the min_size allocation warn or not.
*
* Tries to do opportunistic allocation for memory first trying to allocate
* max_size amount of memory and then splitting that until zero order is
* reached. Allocation is tried without generating allocation warnings unless
* last_warn is set. Last_warn set affects only last allocation of zero order.
*
* By default, five_maxorder is 0 and it is equivalent to kmalloc(GFP_KERNEL)
*
* Return pointer to allocated memory, or NULL on failure.
*/
static void *five_alloc_pages(loff_t max_size, size_t *allocated_size,
int last_warn)
{
void *ptr;
int order = five_maxorder;
gfp_t gfp_mask = __GFP_RECLAIM | __GFP_NOWARN | __GFP_NORETRY;
if (order)
order = min(get_order(max_size), order);
for (; order; order--) {
ptr = (void *)__get_free_pages(gfp_mask, order);
if (ptr) {
*allocated_size = PAGE_SIZE << order;
return ptr;
}
}
/* order is zero - one page */
gfp_mask = GFP_KERNEL;
if (!last_warn)
gfp_mask |= __GFP_NOWARN;
ptr = (void *)__get_free_pages(gfp_mask, 0);
if (ptr) {
*allocated_size = PAGE_SIZE;
return ptr;
}
*allocated_size = 0;
return NULL;
}
/**
* five_free_pages() - Free pages allocated by five_alloc_pages().
* @ptr: Pointer to allocated pages.
* @size: Size of allocated buffer.
*/
static void five_free_pages(void *ptr, size_t size)
{
if (!ptr)
return;
free_pages((unsigned long)ptr, get_order(size));
}
static struct crypto_ahash *five_alloc_atfm(enum hash_algo algo)
{
struct crypto_ahash *tfm = five_ahash_tfm;
int rc;
if (algo < 0 || algo >= HASH_ALGO__LAST)
algo = five_hash_algo;
if (algo != five_hash_algo || !tfm) {
tfm = crypto_alloc_ahash(hash_algo_name[algo], 0, 0);
if (!IS_ERR(tfm)) {
if (algo == five_hash_algo)
five_ahash_tfm = tfm;
} else {
rc = PTR_ERR(tfm);
pr_err("Can not allocate %s (reason: %d)\n",
hash_algo_name[algo], rc);
}
}
return tfm;
}
static void five_free_atfm(struct crypto_ahash *tfm)
{
if (tfm != five_ahash_tfm)
crypto_free_ahash(tfm);
}
static void ahash_complete(struct crypto_async_request *req, int err)
{
struct ahash_completion *res = req->data;
if (err == -EINPROGRESS)
return;
res->err = err;
complete(&res->completion);
}
static int ahash_wait(int err, struct ahash_completion *res)
{
try_to_freeze();
switch (err) {
case 0:
break;
case -EINPROGRESS:
case -EBUSY:
wait_for_completion(&res->completion);
reinit_completion(&res->completion);
err = res->err;
fallthrough;
default:
pr_crit_ratelimited("ahash calculation failed: err: %d\n", err);
}
return err;
}
static int five_calc_file_hash_atfm(struct file *file,
u8 *hash, size_t *hash_len,
struct crypto_ahash *tfm)
{
const size_t len = crypto_ahash_digestsize(tfm);
loff_t i_size, offset;
char *rbuf[2] = { NULL, };
int rc, read = 0, rbuf_len, active = 0, ahash_rc = 0;
struct ahash_request *req;
struct scatterlist sg[1];
struct ahash_completion res;
size_t rbuf_size[2];
if (*hash_len < len)
return -EINVAL;
req = ahash_request_alloc(tfm, GFP_KERNEL);
if (!req)
return -ENOMEM;
init_completion(&res.completion);
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP,
ahash_complete, &res);
rc = ahash_wait(crypto_ahash_init(req), &res);
if (rc)
goto out1;
i_size = i_size_read(file_inode(file));
if (i_size == 0)
goto out2;
/*
* Try to allocate maximum size of memory.
* Fail if even a single page cannot be allocated.
*/
rbuf[0] = five_alloc_pages(i_size, &rbuf_size[0], 1);
if (!rbuf[0]) {
rc = -ENOMEM;
goto out1;
}
/* Only allocate one buffer if that is enough. */
if (i_size > rbuf_size[0]) {
/*
* Try to allocate secondary buffer. If that fails fallback to
* using single buffering. Use previous memory allocation size
* as baseline for possible allocation size.
*/
rbuf[1] = five_alloc_pages(i_size - rbuf_size[0],
&rbuf_size[1], 0);
}
if (!(file->f_mode & FMODE_READ)) {
file->f_mode |= FMODE_READ;
read = 1;
}
for (offset = 0; offset < i_size; offset += rbuf_len) {
if (!rbuf[1] && offset) {
/* Not using two buffers, and it is not the first
* read/request, wait for the completion of the
* previous ahash_update() request.
*/
rc = ahash_wait(ahash_rc, &res);
if (rc)
goto out3;
}
/* read buffer */
rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]);
rc = integrity_kernel_read(file, offset, rbuf[active],
rbuf_len);
if (rc != rbuf_len)
goto out3;
if (rbuf[1] && offset) {
/* Using two buffers, and it is not the first
* read/request, wait for the completion of the
* previous ahash_update() request.
*/
rc = ahash_wait(ahash_rc, &res);
if (rc)
goto out3;
}
sg_init_one(&sg[0], rbuf[active], rbuf_len);
ahash_request_set_crypt(req, sg, NULL, rbuf_len);
ahash_rc = crypto_ahash_update(req);
if (rbuf[1])
active = !active; /* swap buffers, if we use two */
}
/* wait for the last update request to complete */
rc = ahash_wait(ahash_rc, &res);
out3:
if (read)
file->f_mode &= ~FMODE_READ;
five_free_pages(rbuf[0], rbuf_size[0]);
five_free_pages(rbuf[1], rbuf_size[1]);
out2:
if (!rc) {
ahash_request_set_crypt(req, NULL, hash, 0);
rc = ahash_wait(crypto_ahash_final(req), &res);
if (!rc)
*hash_len = len;
}
out1:
ahash_request_free(req);
return rc;
}
static int five_calc_file_ahash(struct file *file,
u8 hash_algo, u8 *hash,
size_t *hash_len)
{
struct crypto_ahash *tfm;
int rc;
tfm = five_alloc_atfm(hash_algo);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
rc = five_calc_file_hash_atfm(file, hash, hash_len, tfm);
five_free_atfm(tfm);
return rc;
}
static int five_calc_file_hash_tfm(struct file *file,
u8 *hash, size_t *hash_len,
struct crypto_shash *tfm)
{
SHASH_DESC_ON_STACK(shash, tfm);
const size_t len = crypto_shash_digestsize(tfm);
loff_t i_size, offset = 0;
char *rbuf;
int rc, read = 0;
if (*hash_len < len)
return -EINVAL;
shash->tfm = tfm;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
shash->flags = 0;
#endif
rc = crypto_shash_init(shash);
if (rc != 0)
return rc;
i_size = i_size_read(file_inode(file));
if (i_size == 0)
goto out;
rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!rbuf)
return -ENOMEM;
if (!(file->f_mode & FMODE_READ)) {
file->f_mode |= FMODE_READ;
read = 1;
}
while (offset < i_size) {
int rbuf_len;
rbuf_len = integrity_kernel_read(file, offset, rbuf, PAGE_SIZE);
if (rbuf_len < 0) {
rc = rbuf_len;
break;
}
if (rbuf_len == 0)
break;
offset += rbuf_len;
try_to_freeze();
rc = crypto_shash_update(shash, rbuf, rbuf_len);
if (rc)
break;
}
if (read)
file->f_mode &= ~FMODE_READ;
kfree(rbuf);
out:
if (!rc)
rc = crypto_shash_final(shash, hash);
if (!rc)
*hash_len = len;
return rc;
}
static int five_calc_hash_tfm(const u8 *data, size_t data_len,
u8 *hash, size_t *hash_len, struct crypto_shash *tfm)
{
SHASH_DESC_ON_STACK(shash, tfm);
const size_t len = crypto_shash_digestsize(tfm);
int rc;
if (*hash_len < len || data_len == 0)
return -EINVAL;
shash->tfm = tfm;
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
shash->flags = 0;
#endif
rc = crypto_shash_init(shash);
if (rc != 0)
return rc;
rc = crypto_shash_update(shash, data, data_len);
if (!rc) {
rc = crypto_shash_final(shash, hash);
if (!rc)
*hash_len = len;
}
return rc;
}
static int five_calc_file_shash(struct file *file,
u8 hash_algo,
u8 *hash,
size_t *hash_len)
{
struct crypto_shash *tfm;
int rc;
tfm = five_alloc_tfm(hash_algo);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
rc = five_calc_file_hash_tfm(file, hash, hash_len, tfm);
five_free_tfm(tfm);
return rc;
}
static int five_calc_data_shash(const u8 *data, size_t data_len, u8 hash_algo,
u8 *hash, size_t *hash_len)
{
struct crypto_shash *tfm;
int rc;
tfm = five_alloc_tfm(hash_algo);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
rc = five_calc_hash_tfm(data, data_len, hash, hash_len, tfm);
five_free_tfm(tfm);
return rc;
}
/*
* five_calc_file_hash - calculate file hash
*
* Asynchronous hash (ahash) allows using HW acceleration for calculating
* a hash. ahash performance varies for different data sizes on different
* crypto accelerators. shash performance might be better for smaller files.
* The 'five.ahash_minsize' module parameter allows specifying the best
* minimum file size for using ahash on the system.
*
* If the five.ahash_minsize parameter is not specified, this function uses
* shash for the hash calculation. If ahash fails, it falls back to using
* shash.
*/
int five_calc_file_hash(struct file *file, u8 hash_algo, u8 *hash,
size_t *hash_len)
{
loff_t i_size;
int rc;
i_size = i_size_read(file_inode(file));
if (five_ahash_minsize && i_size >= five_ahash_minsize) {
rc = five_calc_file_ahash(file, hash_algo, hash, hash_len);
if (!rc)
return 0;
}
return five_calc_file_shash(file, hash_algo, hash, hash_len);
}
int five_calc_data_hash(const uint8_t *data, size_t data_len,
uint8_t hash_algo, uint8_t *hash, size_t *hash_len)
{
return five_calc_data_shash(data, data_len, hash_algo, hash, hash_len);
}

View file

@ -1,29 +0,0 @@
/*
* FIVE crypto API
*
* Copyright (C) 2017 Samsung Electronics, Inc.
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_FIVE_CRYPTO_H
#define __LINUX_FIVE_CRYPTO_H
#include <linux/file.h>
int five_init_crypto(void);
int five_calc_file_hash(struct file *file, u8 hash_algo, u8 *hash,
size_t *hash_len);
int five_calc_data_hash(const uint8_t *data, size_t data_len,
uint8_t hash_algo, uint8_t *hash, size_t *hash_len);
#endif // __LINUX_FIVE_CRYPTO_H

View file

@ -1,104 +0,0 @@
/*
* Wrappers for backward compatibility with old Kernels.
*
* Copyright (C) 2019 Samsung Electronics, Inc.
*
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_FIVE_CRYPTO_COMP_H
#define __LINUX_FIVE_CRYPTO_COMP_H
#include <linux/version.h>
#include <crypto/public_key.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0)
static inline int five_verify_signature(struct key *key,
struct public_key_signature *pks,
struct five_cert *cert,
struct five_cert_header *header)
{
int ret = -ENOMEM;
pks->pkey_hash_algo = header->hash_algo;
pks->nr_mpi = 1;
pks->rsa.s = mpi_read_raw_data(cert->signature->value,
cert->signature->length);
if (pks->rsa.s)
ret = verify_signature(key, pks);
mpi_free(pks->rsa.s);
return ret;
}
#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
static inline int five_verify_signature(struct key *key,
struct public_key_signature *pks,
struct five_cert *cert,
struct five_cert_header *header)
{
int ret = -ENOMEM;
pks->pkey_algo = "rsa";
pks->hash_algo = hash_algo_name[header->hash_algo];
pks->s = cert->signature->value;
pks->s_size = cert->signature->length;
ret = verify_signature(key, pks);
return ret;
}
#else
static inline int five_verify_signature(struct key *key,
struct public_key_signature *pks,
struct five_cert *cert,
struct five_cert_header *header)
{
int ret = -ENOMEM;
pks->pkey_algo = "rsa";
pks->encoding = "pkcs1";
pks->hash_algo = hash_algo_name[header->hash_algo];
pks->s = cert->signature->value;
pks->s_size = cert->signature->length;
ret = verify_signature(key, pks);
return ret;
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
static inline struct key *five_keyring_alloc(const char *description,
kuid_t uid, kgid_t gid, const struct cred *cred,
key_perm_t perm, unsigned long flags)
{
return keyring_alloc(description, uid, gid, cred,
perm, flags, NULL);
}
#else
static inline struct key *five_keyring_alloc(const char *description,
kuid_t uid, kgid_t gid, const struct cred *cred,
key_perm_t perm, unsigned long flags)
{
return keyring_alloc(description, uid, gid, cred,
perm, flags, NULL, NULL);
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0)
#define keyring_search(keyring, type, desc, recurse) \
keyring_search(keyring, type, desc)
#endif
#endif /* __LINUX_FIVE_CRYPTO_COMP_H */

View file

@ -1,426 +0,0 @@
/*
* FIVE dmverity functions
*
* Copyright (C) 2019 Samsung Electronics, Inc.
*
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
* Viacheslav Vovchenko <v.vovchenko@samsung.com>
* Yevgen Kopylov <y.kopylov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "five_dmverity.h"
#include "five.h"
#include "five_testing.h"
#include "five_porting.h"
#include "drivers/md/dm.h"
#ifdef CONFIG_FIVE_DEBUG
#include <linux/debugfs.h>
#endif
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) || defined(CONFIG_FIVE_DEBUG)
__visible_for_testing
bool check_prebuilt_paths_dmverity;
#endif
static inline int __init init_fs(void);
__visible_for_testing __mockable
struct mapped_device *call_dm_get_md(dev_t dev)
{
return dm_get_md(dev);
}
__visible_for_testing __mockable
struct dm_table *call_dm_get_live_table(
struct mapped_device *md, int *srcu_idx)
{
return dm_get_live_table(md, srcu_idx);
}
__visible_for_testing __mockable
fmode_t call_dm_table_get_mode(struct dm_table *t)
{
return dm_table_get_mode(t);
}
__visible_for_testing __mockable
unsigned int call_dm_table_get_num_targets(struct dm_table *t)
{
return dm_table_get_num_targets(t);
}
__visible_for_testing __mockable
struct dm_target *call_dm_table_get_target(
struct dm_table *t, unsigned int index)
{
return dm_table_get_target(t, index);
}
__visible_for_testing __mockable
void call_dm_put_live_table(struct mapped_device *md, int srcu_idx)
{
dm_put_live_table(md, srcu_idx);
return;
}
__visible_for_testing __mockable
void call_dm_put(struct mapped_device *md)
{
dm_put(md);
return;
}
__visible_for_testing __mockable
struct block_device *call_blkdev_get_by_dev(
dev_t dev, fmode_t mode, void *holder)
{
return blkdev_get_by_dev(dev, mode, holder);
}
__visible_for_testing __mockable
void call_blkdev_put(struct block_device *bdev, fmode_t mode)
{
blkdev_put(bdev, mode);
return;
}
int __init five_init_dmverity(void)
{
return init_fs();
}
__visible_for_testing
bool is_loop_device(const struct file *file)
{
const struct inode *inode;
inode = file_inode(file);
if (unlikely(!inode || !inode->i_sb))
return false;
return MAJOR(inode->i_sb->s_dev) == LOOP_MAJOR ? true : false;
}
/* Check whether the file belongs to the device mapper's enabled target
* Need to check:
* - Super block points to a block device.
* - The block device is created by the device mapper
* (dm_get_md/dm_put_md functions).
* - The block device is a disk with a name "dm-[1-9]".
* - The disk is in readonly mode.
* - There is only one enabled target in the device mapper table.
* - The target name is known target name.
*/
__visible_for_testing
enum five_dmverity_codes is_dmverity_partition(
const struct file *file)
{
const char dm_dev_prefix[] = "dm-";
const char * const dm_targets_name[] = {
"verity", "verity-fec"
};
enum five_dmverity_codes result = FIVE_DMV_NO_DM_TARGET_NAME;
int srcu_idx;
unsigned int num_targets;
size_t i;
const struct inode *inode;
struct super_block *i_sb;
struct mapped_device *md;
struct gendisk *disk;
struct dm_table *table;
struct dm_target *target;
inode = file_inode(file);
if (unlikely(!inode)) {
result = FIVE_DMV_BAD_INPUT;
goto exit;
}
i_sb = inode->i_sb;
if (unlikely(!i_sb)) {
result = FIVE_DMV_BAD_INPUT;
goto exit;
}
md = call_dm_get_md(i_sb->s_dev);
if (!md) {
result = FIVE_DMV_NO_DM_DEVICE;
goto exit;
}
disk = dm_disk(md);
if (!disk) {
result = FIVE_DMV_NO_DM_DISK;
goto exit_free_dm;
}
if (!get_disk_ro(disk)) {
result = FIVE_DMV_NOT_READONLY_DM_DISK;
goto exit_free_dm;
}
if (strncmp(disk->disk_name, dm_dev_prefix,
sizeof(dm_dev_prefix) - 1)) {
result = FIVE_DMV_BAD_DM_PREFIX_NAME;
goto exit_free_dm;
}
table = call_dm_get_live_table(md, &srcu_idx);
if (!table) {
result = FIVE_DMV_NO_DM_TABLE;
goto exit_free_dm;
}
if (call_dm_table_get_mode(table) & ~FMODE_READ) {
result = FIVE_DMV_NOT_READONLY_DM_TABLE;
goto exit_free_table;
}
num_targets = call_dm_table_get_num_targets(table);
target = call_dm_table_get_target(table, 0);
if (!target) {
result = FIVE_DMV_NO_DM_TARGET;
goto exit_free_table;
}
/* Only support devices that have a single target */
if (num_targets != 1) {
result = FIVE_DMV_NOT_SINGLE_TARGET;
goto exit_free_table;
}
/* Checks the specific target name from available targets
* in device mapper.
*/
for (i = 0; i < ARRAY_SIZE(dm_targets_name); ++i) {
if (!strncmp(target->type->name, dm_targets_name[i],
strlen(dm_targets_name[i]) + 1)) {
result = FIVE_DMV_PARTITION;
break;
}
}
exit_free_table:
call_dm_put_live_table(md, srcu_idx);
exit_free_dm:
call_dm_put(md);
exit:
return result;
}
__visible_for_testing
enum five_dmverity_codes is_dmverity_loop(
const struct file *file)
{
const fmode_t mode_bdev = FMODE_READ;
enum five_dmverity_codes result = FIVE_DMV_NO_SB_LOOP_DEVICE;
struct super_block *i_sb;
const struct inode *inode;
struct block_device *bdev;
struct gendisk *bd_disk;
struct file *file_mount_to_lo_dev;
struct loop_device *lo_dev;
inode = file_inode(file);
i_sb = inode->i_sb;
if (MAJOR(i_sb->s_dev) != LOOP_MAJOR)
goto exit;
bdev = call_blkdev_get_by_dev(i_sb->s_dev, mode_bdev, NULL);
if (IS_ERR_OR_NULL(bdev) || MAJOR(bdev->bd_dev) != LOOP_MAJOR) {
result = FIVE_DMV_NO_BD_LOOP_DEVICE;
goto exit;
}
bd_disk = bdev->bd_disk;
if (unlikely(!bd_disk)) {
result = FIVE_DMV_NO_BD_DISK;
goto exit_free_blkdev;
}
lo_dev = bd_disk->private_data;
if (unlikely(!lo_dev)) {
result = FIVE_DMV_NO_LOOP_DEV;
goto exit_free_blkdev;
}
file_mount_to_lo_dev = lo_dev->lo_backing_file;
if (unlikely(!file_mount_to_lo_dev)) {
result = FIVE_DMV_NO_LOOP_BACK_FILE;
goto exit_free_blkdev;
}
result = is_dmverity_partition(file_mount_to_lo_dev);
exit_free_blkdev:
call_blkdev_put(bdev, mode_bdev);
exit:
return result;
}
#if defined(CONFIG_SAMSUNG_PRODUCT_SHIP) && !defined(CONFIG_FIVE_DEBUG)
bool five_is_dmverity_protected(const struct file *file)
{
enum five_dmverity_codes dmv_file_status;
bool result = false;
if (unlikely(!file))
return result;
/* Checks is loop device /dev/block/loopX or no */
if (is_loop_device(file))
/* Checks /dev/block/loopX mounted to /apex/name@version and
* it should specify to /system/apex/[name].apex under dmverity
* protection.
*/
dmv_file_status = is_dmverity_loop(file);
else
/* Checks file on dmverity partition, in case of dmverity is
* disabled need to check prebuilt paths of dmverity.
*/
dmv_file_status = is_dmverity_partition(file);
if (dmv_file_status == FIVE_DMV_PARTITION)
result = true;
return result;
}
#else
static bool is_dmverity_prebuit_path(const struct file *file)
{
const char * const paths[] = {
"/system/", "/system_ext/", "/vendor/", "/apex/",
"/product/", "/odm/", "/prism/", "/optics/"
};
const char *pathname = NULL;
char *pathbuf = NULL;
char filename[NAME_MAX];
bool result = false;
size_t i;
pathname = five_d_path(&file->f_path, &pathbuf, filename);
for (i = 0; i < ARRAY_SIZE(paths); ++i) {
if (!strncmp(pathname, paths[i], strlen(paths[i]))) {
result = true;
break;
}
}
if (pathbuf)
__putname(pathbuf);
return result;
}
bool five_is_dmverity_protected(const struct file *file)
{
enum five_dmverity_codes dmv_file_status;
bool result = false;
if (unlikely(!file))
return result;
if (check_prebuilt_paths_dmverity)
return is_dmverity_prebuit_path(file);
/* Checks is loop device /dev/block/loopX or no */
if (is_loop_device(file))
/* Checks /dev/block/loopX mounted to /apex/name@version/ and
* it should specify to /system/apex/[name].apex under dmverity
* protection.
*/
dmv_file_status = is_dmverity_loop(file);
else
/* Checks file on dmverity partition, in case of dmverity is
* disabled need to check prebuilt paths dmverity.
*/
dmv_file_status = is_dmverity_partition(file);
if (dmv_file_status == FIVE_DMV_PARTITION)
result = true;
else
result = is_dmverity_prebuit_path(file);
return result;
}
#endif
#ifdef CONFIG_FIVE_DEBUG
static ssize_t five_check_prebuilt_paths_dmverity_read(struct file *file,
char __user *user_buf, size_t count, loff_t *pos)
{
char buf[2];
buf[0] = check_prebuilt_paths_dmverity ? '1' : '0';
buf[1] = '\n';
return simple_read_from_buffer(user_buf, count, pos, buf, sizeof(buf));
}
static ssize_t five_check_prebuilt_paths_dmverity_write(struct file *file,
const char __user *buf, size_t count, loff_t *pos)
{
char command;
if (get_user(command, buf))
return -EFAULT;
switch (command) {
case '0':
check_prebuilt_paths_dmverity = false;
break;
case '1':
check_prebuilt_paths_dmverity = true;
break;
default:
pr_err("FIVE: %s: unknown cmd: %hhx\n", __func__, command);
return -EINVAL;
}
pr_info("FIVE debug: Check dm-verity file paths %s\n",
check_prebuilt_paths_dmverity ? "enabled" : "disabled");
return count;
}
static const struct file_operations five_dmverity_fops = {
.owner = THIS_MODULE,
.read = five_check_prebuilt_paths_dmverity_read,
.write = five_check_prebuilt_paths_dmverity_write
};
static inline int __init init_fs(void)
{
struct dentry *debug_file = NULL;
const umode_t umode = 0664;
debug_file = debugfs_create_file("five_prebuilt_paths_dmverity",
umode, NULL, NULL, &five_dmverity_fops);
if (IS_ERR_OR_NULL(debug_file))
goto error;
return 0;
error:
if (debug_file)
return -PTR_ERR(debug_file);
return -EEXIST;
}
#else
static inline int __init init_fs(void)
{
return 0;
}
#endif

View file

@ -1,49 +0,0 @@
/*
* FIVE dmverity functions
*
* Copyright (C) 2019 Samsung Electronics, Inc.
*
* Egor Uleyskiy, <e.uleyskiy@samsung.com>
* Viacheslav Vovchenko <v.vovchenko@samsung.com>
* Yevgen Kopylov <y.kopylov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_FIVE_DMVERITY_H
#define __LINUX_FIVE_DMVERITY_H
#include <linux/init.h>
struct file;
enum five_dmverity_codes {
FIVE_DMV_PARTITION,
FIVE_DMV_BAD_INPUT,
FIVE_DMV_NO_DM_DEVICE,
FIVE_DMV_NO_DM_DISK,
FIVE_DMV_NOT_READONLY_DM_DISK,
FIVE_DMV_BAD_DM_PREFIX_NAME,
FIVE_DMV_NO_DM_TABLE,
FIVE_DMV_NOT_READONLY_DM_TABLE,
FIVE_DMV_NO_DM_TARGET,
FIVE_DMV_NOT_SINGLE_TARGET,
FIVE_DMV_NO_DM_TARGET_NAME,
FIVE_DMV_NO_SB_LOOP_DEVICE,
FIVE_DMV_NO_BD_LOOP_DEVICE,
FIVE_DMV_NO_BD_DISK,
FIVE_DMV_NO_LOOP_DEV,
FIVE_DMV_NO_LOOP_BACK_FILE
};
int __init five_init_dmverity(void);
bool five_is_dmverity_protected(const struct file *file);
#endif // __LINUX_FIVE_DMVERITY_H

View file

@ -1,183 +0,0 @@
/*
* FIVE-DSMS integration.
*
* Copyright (C) 2020 Samsung Electronics, Inc.
* Yevgen Kopylov, <y.kopylov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/workqueue.h>
#include <linux/string.h>
#include <linux/crc16.h>
#include "five.h"
#include "five_testing.h"
#ifndef FIVE_KUNIT_ENABLED
#include <linux/dsms.h>
#include "five_dsms.h"
#endif
#define MESSAGE_BUFFER_SIZE 600
#define MESSAGE_FIVE_INIT_SIZE 32
#define DEFERRED_TIME (1000 * 300)
#define MAX_FIV1_NUM 16
#define MAX_FIV2_NUM 96
#define FIV3_FIRST 1
#define FIV3_FEW 20
#define FIV3_LOT 100
struct sign_err_event {
char comm[TASK_COMM_LEN];
int result;
int count;
};
static struct five_dsms_report_context {
struct delayed_work work;
char message[MESSAGE_FIVE_INIT_SIZE];
} context;
static DEFINE_SPINLOCK(five_dsms_lock);
static struct sign_err_event sign_err_events[MAX_FIV1_NUM];
static bool fiv3_overflow;
static DECLARE_BITMAP(mask, MAX_FIV2_NUM);
static bool oem_unlocking_state __ro_after_init;
static int __init verifiedboot_state_setup(char *str)
{
static const char unlocked[] = "orange";
oem_unlocking_state = !strncmp(str, unlocked, sizeof(unlocked));
if (oem_unlocking_state)
pr_err("FIVE: Device is unlocked\n");
return 0;
}
__setup("androidboot.verifiedbootstate=", verifiedboot_state_setup);
// proxy to call kernel func
u16 __mockable call_crc16(u16 crc, u8 const *buffer, size_t len)
{
return crc16(crc, buffer, len);
}
/*`noinline` is required by DSMS.
* Don't rename this function. File_name/function_name
* is used by DSMS in dsms_whitelist as an access rule.
*/
noinline void __mockable five_dsms_msg(const char *tag, const char *msg)
{
int ret;
ret = dsms_send_message(tag, msg, 0);
if (ret)
pr_err("FIVE: unable to send dsms message(result:%d)\n", ret);
}
static void five_dsms_init_report(struct work_struct *in_data)
{
struct five_dsms_report_context *context = container_of(in_data,
struct five_dsms_report_context, work.work);
five_dsms_msg("FIV0", context->message);
}
void five_dsms_sign_err(const char *app, int result)
{
int i, current_count;
struct sign_err_event *same_event = NULL;
char dsms_msg[MESSAGE_BUFFER_SIZE];
bool send_overflow = false;
spin_lock(&five_dsms_lock);
for (i = 0; i < MAX_FIV1_NUM; i++) {
if (sign_err_events[i].count == 0) {
same_event = &sign_err_events[i];
current_count = ++same_event->count;
strlcpy(same_event->comm, app, TASK_COMM_LEN);
same_event->result = result;
break;
} else if (sign_err_events[i].result == result &&
!strncmp(sign_err_events[i].comm, app, TASK_COMM_LEN)) {
same_event = &sign_err_events[i];
current_count = ++same_event->count;
break;
}
}
if (!same_event && !fiv3_overflow) {
fiv3_overflow = true;
send_overflow = true;
}
spin_unlock(&five_dsms_lock);
if (same_event) {
switch (current_count) {
case FIV3_FIRST:
case FIV3_FEW:
case FIV3_LOT:
snprintf(dsms_msg, MESSAGE_BUFFER_SIZE,
"%s res = %d count = %d", same_event->comm,
same_event->result, current_count);
five_dsms_msg("FIV3", dsms_msg);
}
} else if (unlikely(send_overflow)) {
five_dsms_msg("FIV3", "data buffer overflow");
}
}
void five_dsms_reset_integrity(const char *task_name, int result,
const char *file_name)
{
char dsms_msg[MESSAGE_BUFFER_SIZE];
unsigned short crc;
int msg_size;
bool sent = true;
if (oem_unlocking_state)
return;
msg_size = snprintf(dsms_msg, MESSAGE_BUFFER_SIZE, "%s|%d|%s",
task_name, result, file_name ? kbasename(file_name) : "");
if (unlikely(msg_size < 0)) {
pr_err("FIVE: unable to create dsms message from task_name: %s, result: %d, file_name: %s\n",
task_name, result, file_name);
return;
}
if (unlikely(msg_size >= MESSAGE_BUFFER_SIZE)) {
pr_warn("FIVE: dsms message size: %d exceeds max buffer size: %d. The tail was truncated! Resulting message is: %s\n",
msg_size, MESSAGE_BUFFER_SIZE, dsms_msg);
msg_size = MESSAGE_BUFFER_SIZE - 1;
}
crc = call_crc16(0, dsms_msg, msg_size) % MAX_FIV2_NUM;
spin_lock(&five_dsms_lock);
if (!test_bit(crc, mask)) {
sent = false;
set_bit(crc, mask);
}
spin_unlock(&five_dsms_lock);
if (!sent)
five_dsms_msg("FIV2", dsms_msg);
return;
}
void five_dsms_init(const char *version, int result)
{
snprintf(context.message, MESSAGE_FIVE_INIT_SIZE, "%s", version);
INIT_DELAYED_WORK(&context.work, five_dsms_init_report);
schedule_delayed_work(&context.work, msecs_to_jiffies(DEFERRED_TIME));
}

View file

@ -1,42 +0,0 @@
/*
* FIVE-DSMS integration.
*
* Copyright (C) 2020 Samsung Electronics, Inc.
* Yevgen Kopylov, <y.kopylov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_FIVE_DSMS_H
#define __LINUX_FIVE_DSMS_H
#ifdef CONFIG_FIVE_DSMS
void five_dsms_sign_err(const char *app, int result);
void five_dsms_reset_integrity(const char *task_name, int result,
const char *file_name);
void five_dsms_init(const char *version, int result);
#else
static inline void five_dsms_sign_err(const char *app, int result)
{
}
static inline void five_dsms_reset_integrity(const char *task_name, int result,
const char *file_name)
{
}
static inline void five_dsms_init(const char *version, int result)
{
pr_debug("FIVE: DSMS is not supported\n");
}
#endif
#endif

View file

@ -1,318 +0,0 @@
/*
* Five Event interface
*
* Copyright (C) 2018 Samsung Electronics, Inc.
* Ivan Vorobiov, <i.vorobiov@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "five_hooks.h"
#include "five_porting.h"
#include "five_testing.h"
#include <linux/fs.h>
#include <linux/slab.h>
#define call_void_hook(FUNC, ...) \
do { \
struct five_hook_list *P; \
\
list_for_each_entry(P, &five_hook_heads.FUNC, list) \
P->hook.FUNC(__VA_ARGS__); \
} while (0)
struct five_hook_heads five_hook_heads = {
.file_processed =
LIST_HEAD_INIT(five_hook_heads.file_processed),
.file_skipped =
LIST_HEAD_INIT(five_hook_heads.file_skipped),
.file_signed =
LIST_HEAD_INIT(five_hook_heads.file_signed),
.task_forked =
LIST_HEAD_INIT(five_hook_heads.task_forked),
.integrity_reset =
LIST_HEAD_INIT(five_hook_heads.integrity_reset),
.integrity_reset2 =
LIST_HEAD_INIT(five_hook_heads.integrity_reset2),
};
EXPORT_SYMBOL_GPL(five_hook_heads);
enum five_hook_event {
FILE_PROCESSED,
FILE_SKIPPED,
FILE_SIGNED,
TASK_FORKED,
INTEGRITY_RESET
};
struct hook_wq_event {
enum five_hook_event event;
union {
struct {
struct task_struct *task;
enum task_integrity_value tint_value;
struct file *file;
void *xattr;
size_t xattr_size;
int result;
} processed;
struct {
struct task_struct *task;
enum task_integrity_value tint_value;
struct file *file;
} skipped;
struct {
struct task_struct *parent;
enum task_integrity_value parent_tint_value;
struct task_struct *child;
enum task_integrity_value child_tint_value;
} forked;
struct {
struct task_struct *task;
struct file *file;
enum task_integrity_reset_cause cause;
} reset;
};
};
static void hook_wq_event_destroy(struct hook_wq_event *event)
{
switch (event->event) {
case FILE_PROCESSED: {
fput(event->processed.file);
put_task_struct(event->processed.task);
kfree(event->processed.xattr);
break;
}
case FILE_SKIPPED: {
fput(event->skipped.file);
put_task_struct(event->skipped.task);
break;
}
case FILE_SIGNED: {
fput(event->processed.file);
put_task_struct(event->processed.task);
kfree(event->processed.xattr);
break;
}
case TASK_FORKED: {
put_task_struct(event->forked.parent);
put_task_struct(event->forked.child);
break;
}
case INTEGRITY_RESET: {
if (event->reset.file)
fput(event->reset.file);
put_task_struct(event->reset.task);
break;
}
}
}
struct hook_wq_context {
struct work_struct data_work;
struct hook_wq_event payload;
};
static struct workqueue_struct *g_hook_workqueue;
static void hook_handler(struct work_struct *in_data)
{
struct hook_wq_event *event;
struct hook_wq_context *context = container_of(in_data,
struct hook_wq_context, data_work);
if (unlikely(!context))
return;
event = &context->payload;
switch (event->event) {
case FILE_PROCESSED: {
call_void_hook(file_processed,
event->processed.task,
event->processed.tint_value,
event->processed.file,
event->processed.xattr,
event->processed.xattr_size,
event->processed.result);
break;
}
case FILE_SKIPPED: {
call_void_hook(file_skipped,
event->skipped.task,
event->skipped.tint_value,
event->skipped.file);
break;
}
case FILE_SIGNED: {
call_void_hook(file_signed,
event->processed.task,
event->processed.tint_value,
event->processed.file,
event->processed.xattr,
event->processed.xattr_size,
event->processed.result);
break;
}
case TASK_FORKED: {
call_void_hook(task_forked,
event->forked.parent,
event->forked.parent_tint_value,
event->forked.child,
event->forked.child_tint_value);
break;
}
case INTEGRITY_RESET: {
call_void_hook(integrity_reset,
event->reset.task);
call_void_hook(integrity_reset2, event->reset.task,
event->reset.file, event->reset.cause);
break;
}
}
hook_wq_event_destroy(event);
kfree(context);
}
static int __push_event(struct hook_wq_event *event, gfp_t flags)
{
struct hook_wq_context *context;
if (!g_hook_workqueue)
return -ENAVAIL;
context = kmalloc(sizeof(struct hook_wq_context), flags);
if (unlikely(!context))
return -ENOMEM;
context->payload = *event;
INIT_WORK(&context->data_work, hook_handler);
return queue_work(g_hook_workqueue, &context->data_work) ? 0 : 1;
}
void five_hook_file_processed(struct task_struct *task,
struct file *file, void *xattr,
size_t xattr_size, int result)
{
struct hook_wq_event event = {0};
event.event = FILE_PROCESSED;
get_task_struct(task);
get_file(file);
event.processed.task = task;
event.processed.tint_value = task_integrity_read(TASK_INTEGRITY(task));
event.processed.file = file;
/*
* xattr parameters are optional, because FIVE could get results
* from cache where xattr absents, so we may ignore kmemdup errors
*/
if (xattr) {
event.processed.xattr = kmemdup(xattr, xattr_size, GFP_KERNEL);
if (event.processed.xattr)
event.processed.xattr_size = xattr_size;
}
event.processed.result = result;
if (__push_event(&event, GFP_KERNEL) < 0)
hook_wq_event_destroy(&event);
}
void five_hook_file_signed(struct task_struct *task,
struct file *file, void *xattr,
size_t xattr_size, int result)
{
struct hook_wq_event event = {0};
event.event = FILE_SIGNED;
get_task_struct(task);
get_file(file);
event.processed.task = task;
event.processed.tint_value = task_integrity_read(TASK_INTEGRITY(task));
event.processed.file = file;
/* xattr parameters are optional, so we may ignore kmemdup errors */
if (xattr) {
event.processed.xattr = kmemdup(xattr, xattr_size, GFP_KERNEL);
if (event.processed.xattr)
event.processed.xattr_size = xattr_size;
}
event.processed.result = result;
if (__push_event(&event, GFP_KERNEL) < 0)
hook_wq_event_destroy(&event);
}
void five_hook_file_skipped(struct task_struct *task, struct file *file)
{
struct hook_wq_event event = {0};
event.event = FILE_SKIPPED;
get_task_struct(task);
get_file(file);
event.skipped.task = task;
event.skipped.tint_value = task_integrity_read(TASK_INTEGRITY(task));
event.skipped.file = file;
if (__push_event(&event, GFP_KERNEL) < 0)
hook_wq_event_destroy(&event);
}
void five_hook_task_forked(struct task_struct *parent,
struct task_struct *child)
{
struct hook_wq_event event = {0};
event.event = TASK_FORKED;
get_task_struct(parent);
get_task_struct(child);
event.forked.parent = parent;
event.forked.parent_tint_value =
task_integrity_read(TASK_INTEGRITY(parent));
event.forked.child = child;
event.forked.child_tint_value =
task_integrity_read(TASK_INTEGRITY(child));
if (__push_event(&event, GFP_ATOMIC) < 0)
hook_wq_event_destroy(&event);
}
int five_hook_wq_init(void)
{
g_hook_workqueue = alloc_ordered_workqueue("%s",
WQ_MEM_RECLAIM | WQ_FREEZABLE, "five_hook_wq");
if (!g_hook_workqueue)
return -ENOMEM;
return 0;
}
__mockable
void five_hook_integrity_reset(struct task_struct *task,
struct file *file,
enum task_integrity_reset_cause cause)
{
struct hook_wq_event event = {0};
if (task == NULL)
return;
event.event = INTEGRITY_RESET;
get_task_struct(task);
if (file)
get_file(file);
event.reset.task = task;
event.reset.file = file;
event.reset.cause = cause;
if (__push_event(&event, GFP_KERNEL) < 0)
hook_wq_event_destroy(&event);
}

Some files were not shown because too many files have changed in this diff Show more