很久以前接触到日系手机内核里面各式各样的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);

知识共享许可协议
本文及其附件均采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。

添加新评论