admin管理员组文章数量:1614997
给自己打个广告,欢迎走过路过的关注一下我的个人主页: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/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/p/352103792
https://cxymm/article/qq_34412985/106541598
版权声明:本文标题:Linux安全模块(LSM)入门及Yama源码分析 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dianzi/1728693308a1169866.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论