很久以前接触到日系手机内核里面各式各样的LSM的时候,就对LSM背后到底是什么,产生了一些奇怪的兴趣。
最近有些得空,读了一些关于LSM,钩子,内核相关的东西,再搭上以前对FJSEC这些OEM LSM的逆向的经验。
尝试胡乱弄个LSM出来看看?也许有用也也许没用,又成为了一个慢慢挖坑再填坑的作品。
为啥叫NGLSM?NG是我很早用的一个网名,全称是Nango,不懂当初为什么会想到这个名字,但是既然想到了,那么就这样叫吧。
// 2023.06.08-00.00.01/FIRST RELEASE
// 2023.06.11-00.01.01/ADD PROTECT OF SETSOCKOPT/GETSOCKOPT
#include <linux/lsm_hooks.h>
#include <linux/path.h>
#include <linux/cred.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/bprm.h>
#include <linux/socket.h>
#include <asm/uaccess.h>
#include <linux/seq_file.h>
#define STATUS_FILE "/tmp/lsm_status"
#define STATUS_PERMISSIVE "PER"
#define STATUS_ENFORCING "ENF"
#define STATUS_ENFORCING_MMAP "ENF2"
#define STATUS_MAX 100
static int nglsm_status(void)
{
struct file *f;
char buf[STATUS_MAX];
mm_segment_t fs;
int status = -1; // unknown
f = filp_open(STATUS_FILE, O_RDONLY, 0);
if (IS_ERR(f))
{
return status;
}
fs = get_fs();
set_fs(get_ds());
f->f_op->read(f, buf, STATUS_MAX, &f->f_pos);
set_fs(fs);
if (strncmp(buf, STATUS_PERMISSIVE, strlen(STATUS_PERMISSIVE)) == 0)
{
status = 0; // permissive
}
else if (strncmp(buf, STATUS_ENFORCING, strlen(STATUS_ENFORCING)) == 0)
{
status = 1; // enforcing
}
else if (strncmp(buf, STATUS_ENFORCING_MMAP, strlen(STATUS_ENFORCING_MMAP)) == 0)
{
status = 2; // enforcing mmap
}
filp_close(f, NULL);
return status;
}
static int nglsm_sb_mount(const char *dev_name, struct path *path,
const char *type, unsigned long flags, void *data)
{
int status = nglsm_status();
if (status == 0)
{
return 0;
}
printk(KERN_ERR "[NGLSM]Mount was not allowed\n");
return -EPERM;
}
static int nglsm_sb_umount(struct vfsmount *mnt, int flags)
{
int status = nglsm_status();
if (status == 0)
{
return 0;
}
printk(KERN_ERR "[NGLSM]UMount was not allowed\n");
return -EPERM;
}
static int nglsm_sb_pivotroot(struct path *old_path, struct path *new_path)
{
int status = nglsm_status();
if (status == 0)
{
return 0;
}
printk(KERN_ERR "[NGLSM]PivotRoot was not allowed\n");
return -EPERM;
}
static int nglsm_socket_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
{
int status = nglsm_status();
if (status == 0)
{
return 0;
}
printk(KERN_ERR "[NGLSM]GetSockOpt was not allowed\n");
return -EPERM;
}
static int nglsm_socket_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
{
int status = nglsm_status();
if (status == 0)
{
return 0;
}
printk(KERN_ERR "[NGLSM]SetSockOpt was not allowed\n");
return -EPERM;
}
static struct security_hook_list nglsm_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(sb_mount, nglsm_sb_mount),
LSM_HOOK_INIT(sb_umount, nglsm_sb_umount),
LSM_HOOK_INIT(sb_pivotroot, nglsm_sb_pivotroot),
LSM_HOOK_INIT(socket_getsockopt, nglsm_socket_getsockopt),
LSM_HOOK_INIT(socket_setsockopt, nglsm_socket_setsockopt),
};
static int lsm_status_proc_show(struct seq_file *m, void *v)
{
int status = nglsm_status();
switch (status)
{
case 0:
seq_printf(m, "Permissive\n");
break;
case 1:
seq_printf(m, "Enforcing\n");
break;
case 2:
seq_printf(m, "Enforcing Stage 2\n");
break;
default:
seq_printf(m, "Unknown\n");
}
return 0;
}
static int lsm_status_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, lsm_status_proc_show, NULL);
}
static const struct file_operations lsm_status_proc_fops = {
.owner = THIS_MODULE,
.open = lsm_status_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static __init int nglsm_init(void)
{
security_add_hooks(nglsm_hooks, ARRAY_SIZE(nglsm_hooks), "nglsm");
proc_create("lsm_status", 0, NULL, &lsm_status_proc_fops);
printk(KERN_INFO "[NGLSM]LOADED\n");
return 0;
}
security_initcall(nglsm_init);