Linux安全模块(LSM)入门及Yama源码分析

给自己打个广告,欢迎走过路过的关注一下我的个人主页:zhangxin00.github.io

LSM是什么

由于Linux内核开发中安全人员的边缘地位(误),安全模块并没有并入主线,而是作为单独的模块存在。
LSM的设计思想是在最少改变内核代码的情况下,提供一个能够成功实现强制访问控制模块需要的结构或者接口。LSM对内核主要有5处改动:

1、在特定的内核数据结构中加入安全域;

inode结构体定义在include/linux/fs.h中。

/*
 * Keep mostly read-only and often accessed (especially for
 * the RCU path lookup and 'stat' data) fields at the beginning
 * of the 'struct inode'
 */
struct inode {
	umode_t			i_mode;
	unsigned short		i_opflags;
	kuid_t			i_uid;
	kgid_t			i_gid;
	unsigned int		i_flags;

#ifdef CONFIG_FS_POSIX_ACL
	struct posix_acl	*i_acl;
	struct posix_acl	*i_default_acl;
#endif

	const struct inode_operations	*i_op;
	struct super_block	*i_sb;
	struct address_space	*i_mapping;
	
--------------------------------------------------------
#ifdef CONFIG_SECURITY
	void			*i_security;
#endif
--------------------------------------------------------
	/* Stat data, not accessed from path walking */
	unsigned long		i_ino;
	/*
	 * Filesystems may only read i_nlink directly.  They shall use the
	 * following functions for modification:
	 *
	 *    (set|clear|inc|drop)_nlink
	 *    inode_(inc|dec)_link_count
	 */
	union {
		const unsigned int i_nlink;
		unsigned int __i_nlink;
	};
	dev_t			i_rdev;
	loff_t			i_size;
	struct timespec64	i_atime;
	struct timespec64	i_mtime;
	struct timespec64	i_ctime;
	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
	unsigned short          i_bytes;
	u8			i_blkbits;
	u8			i_write_hint;
	blkcnt_t		i_blocks;

#ifdef __NEED_I_SIZE_ORDERED
	seqcount_t		i_size_seqcount;
#endif

	/* Misc */
	unsigned long		i_state;
	struct rw_semaphore	i_rwsem;

	unsigned long		dirtied_when;	/* jiffies of first dirtying */
	unsigned long		dirtied_time_when;

	struct hlist_node	i_hash;
	struct list_head	i_io_list;	/* backing dev IO list */
#ifdef CONFIG_CGROUP_WRITEBACK
	struct bdi_writeback	*i_wb;		/* the associated cgroup wb */

	/* foreign inode detection, see wbc_detach_inode() */
	int			i_wb_frn_winner;
	u16			i_wb_frn_avg_time;
	u16			i_wb_frn_history;
#endif
	struct list_head	i_lru;		/* inode LRU list */
	struct list_head	i_sb_list;
	struct list_head	i_wb_list;	/* backing dev writeback list */
	union {
		struct hlist_head	i_dentry;
		struct rcu_head		i_rcu;
	};
	atomic64_t		i_version;
	atomic64_t		i_sequence; /* see futex */
	atomic_t		i_count;
	atomic_t		i_dio_count;
	atomic_t		i_writecount;
#ifdef CONFIG_IMA
	atomic_t		i_readcount; /* struct files open RO */
#endif
	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
	struct file_lock_context	*i_flctx;
	struct address_space	i_data;
	struct list_head	i_devices;
	union {
		struct pipe_inode_info	*i_pipe;
		struct block_device	*i_bdev;
		struct cdev		*i_cdev;
		char			*i_link;
		unsigned		i_dir_seq;
	};

	__u32			i_generation;

#ifdef CONFIG_FSNOTIFY
	__u32			i_fsnotify_mask; /* all events this inode cares about */
	struct fsnotify_mark_connector __rcu	*i_fsnotify_marks;
#endif

#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
	struct fscrypt_info	*i_crypt_info;
#endif

	void			*i_private; /* fs or device private pointer */
} __randomize_layout;

2、在内核源代码中不同的关键点插入对安全钩子函数的调用

这些安全钩子函数定义在security/security.c中。
在这里插入图片描述
在LSM框架下,每个安全模块需要实现一个"security_hook_list"结构体数组,每个数组项表示一个实现了的hook函数。
以Yama为例,yama中仅新增了四个钩子函数:
在这里插入图片描述
结构体定义在include/linux/lsm_hook.h中,在初始化时借助了下面的宏:

include/linux/lsm_hook.h
/*
 * Security module hook list structure.
 * For use with generic list macros for common operations.
 */
struct security_hook_list {
	struct hlist_node		list;
	struct hlist_head		*head;
	union security_list_options	hook;
	char				*lsm;
} __randomize_layout;

/*
 * Initializing a security_hook_list structure takes
 * up a lot of space in a source file. This macro takes
 * care of the common case and reduces the amount of
 * text involved.
 */
#define LSM_HOOK_INIT(HEAD, HOOK) \
	{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }

3、加入一个通用的安全系统调用

Linux允许安全模块为安全相关的应用编写新的系统调用,其风格类似于原有的Linux系统调用socketcall(),是一个多路的系统调用。

这个系统调用为security(),其参数为(unsigned int id, unsigned int call, unsigned long *args),其中id代表模块描述符,call代表调用描述符,args代表参数列表。

这个系统调用提供了一个sys_security()入口函数:其简单的以参数调用sys_security()钩子函数。

如果安全模块不提供新的系统调用,就可以定义返回-ENOSYS的sys_security()钩子函数,但是大多数安全模块都可以自己定义这个系统调用的实现。

4、提供了函数允许内核模块注册为安全模块或者注销

在加载安全模块时,我们必需先对模块进行注册,我们可以使用register_security()函数向LSM注册一个安全模块。在我们的模块被加载成功后,就可以进行访问控制操作。如果此时还有一个安全模块要使用register_security()函数进行加载,则会出现错误,直到使用unregister_security()函数向框架注销后,下一个模块才可以载入。当然LSM还提供了mod_reg_security()函数和mod_unreg_security()函数,可以连续注册多个安全模块。如果有其他后来的模块需要载入,可以通过mod_reg_security()向第一个模块注册,形成支持不同策略的模块栈

5、将capabilities逻辑的大部分移植为一个可选的安全模块

security文件夹下,每个子文件夹对应一个安全模块。其中除了Yama可以与其他模块兼容,其他模块互不兼容。
在这里插入图片描述
下面是访问对象时,LSM时所处的位置(图片来源于https://cxymm.net/article/qq_34412985/106541598)
在这里插入图片描述
Lilinux安全模块(LSM)主要支持"限制型"的访问控制决策:当Linux内核授予文件或目录访问权限时,Linux安全模块(LSM)可能会拒绝,而当 Linux内核拒绝访问时,可以跳过LSM。

Yama学习

源码分析

1.确定要hook的函数

Yama主要是对Ptrace函数调用进行访问控制。

Yama的目的是对Ptrace进行访问控制。Ptrace是一个系统调用,它提供了一种方法来让‘父’进程可以观察和控制其它进程的执行,检查和改变其核心映像以及寄存器。 主要用来实现断点调试和系统调用跟踪。利用ptrace函数,不仅可以劫持另一个进程的调用,修改系统函数调用和改变返回值,而且可以向另一个函数注入代码,修改eip,进入自己的逻辑。这个函数广泛用于调试和信号跟踪工具。

2.对hook函数进行必要的填充,添加自己的逻辑(实现额外的安全检查)

Yama实现了四个函数

3.添加到在security_hook_list的数据结构里
4.对这个有注册逻辑的函数进行注册

实验测试

LSM机制之所以简单实用,是因为它的设计者几乎罗列了任何可能出现安全问题的地方,然后在这些地方安插钩子,并且这些钩子的实现定义为回调函数,具体的安全引擎只需要自己实现这些钩子函数即可。

在这里插入图片描述
在**/proc/sys/kernel**文件夹下可以看到我们装载的yama模块,在默认设置下yama是默认装载的。
在这里插入图片描述
查看yama默认的等级,为1。
在这里插入图片描述
编写如下代码,并编译为可执行文件yama,来帮助我们测试。

#include<stdio.h>
#include <unistd.h> 
  int main()
  {
    while(1)
    {
        sleep(20);
        static int i = 0;
    }
    return 0;
  }

在超级用户模式下ptrace,成功跟踪目标进程。
在这里插入图片描述
退出超级用户模式,再尝试ptrace,失败,被yama阻止。
在这里插入图片描述

参考

https://zhuanlan.zhihu.com/p/352103792
https://cxymm.net/article/qq_34412985/106541598

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值