目录
0. 引言 1. Linux Security Module Framework Introduction 2. LSM Sourcecode Analysis 3. LSMs Hook Engine:基于LSM Hook进行元数据的监控获取 4. LSM编程示例 5. Linux LSM stacking
0. 引言
从最佳实践的角度来说,在Linux Kernel中进行安全审计、Access Control(访问控制)最好的方式就是使用Linux提供的原生的框架机制,例如
1. Kprobe: Linux提供的原生的调试机制(Debug),允许向任意的内核函数注册Hook回调通知点,当执行到Hooked函数的时候,会触发我们注册的kprobe_handle 2. LSM(Linux Security Modules): Linux提供的原生的Hook框架,本质上来说,LSM是直接串行的插入到了Linux Kernel的代码路径中,是Linux本身对外提供了一套审计机制(Hook框架)
LSM框架的设计初衷是为了在Linux Kernel中实现一个MAC(Mandatory Access Control) Hook框架,LSM已经作为Linux Kernel的一部分随着内核一起发布了,使用框架的好处就在于,安全人员可以直接基于LSM框架进行上层审计模块的开发,专注于业务逻辑,而不需要关注底层的内核差异兼 容性和稳定性,这是相对于sys_call hook方式最大的优势
1. Linux Security Module Framework Introduction
The Linux Security Module (LSM) framework provides a mechanism for various security checks to be hooked by new kernel extensions. The name "module" is a bit of a misnomer since these extensions are not actually loadable kernel modules. Instead, they are selectable at build-time via CONFIG_DEFAULT_SECURITY and can be overridden at boot-time via the "security=..." kernel command line argument, in the case where multiple LSMs were built into a given kernel.
The primary users of the LSM interface are Mandatory Access Control (MAC) extensions which provide a comprehensive security policy. Examples include
1. SELinux 2. Smack 3. Tomoyo 4. AppArmor 5. other extensions can be built using the LSM to provide specific changes to system operation //Linux LSM是Linux下的一个框架标准,并不是一个特定的技术,类似于WINDOWS中的NDIS(Network Driver Interface Specification)概念,而SELINUX是在遵循针这一框架的基础上实现的具体Hook技术,SELINUX和LSM的关系是具体实现和框架标准的关系
0x1: Design
LSM inserts "hooks" (upcalls to the module) at every point in the kernel where a user-level system call is about to result in access to an important internal kernel object such as inodes and task control blocks.
The project is narrowly scoped to solve the problem of access control to avoid imposing a large and complex change patch on the mainstream kernel. It is not intended as a general "hook" or "upcall" mechanism, nor does it support Operating system-level virtualization.
0x2: LSM Principle
The Linux Security Modules (LSM) project has developed a lightweight, general purpose, access control framework for the mainstream Linux kernel that enables many different access control models to be implemented as loadable kernel modules.
A number of existing enhanced access control implementations, including
1. Linux Capabilities Model 2. Security-Enhanced Linux (SELinux) Model 3. Domain and Type Enforcement (DTE) Model
使用LSM Hook框架进行内核安全审计、元数据捕获,安全人员只需要按照既定的调用规范编写LKM模块,并加载进Linux内核,而不需要对system call lookup表进行任何修改
LSM allows modules to mediate access to kernel objects by placing hooks in the kernel code just ahead of the access, as shown in picture above
0x3: LSM Implementation Principle
The LSM kernel patch modifies the kernel in five primary ways
1. it adds opaque security fields to certain kernel data structures 1) task_struct: Task(Process) 2) linux_binprm: Program 3) super_block: Filesystem 4) inode: Pipe、File,、or Socket 5) file: Open File 6) sk_buff: Network Buffer(Packet) 7) net_device: Network Device 8) kern_ipc_perm: Semaphore、Shared Memory Segment、Message Queue 9) msg_msg: Individual Message /* The setting of these security fields and the management of the associated security data is handled by the security modules. LSM merely provides the fields and a set of calls to security hooks that can be implemented by the module to manage the security fields as desired LSM以串行地方式插入到Linux Kernel中,在进行串行Hook的同时,同时自身也需要维护一套数据结构来保存自身的Hook信息,这也是Hook Engine的通用思路 */ 2. the patch inserts calls to security hook functions at various points within the kernel code LSM在Linux Kernel中插入了两种"security hooks function call" 1) LSM安全策略判断 2) lSM自身数据结构维护 这些hook function 指针都保存在一个全局结构体: "security-ops"中(struct security_operations) 3. the patch adds a generic security system call LSM provides a general security system call that allows security modules to implement new calls for security-aware applications 4. the patch provides functions to allow kernel modules to register and unregister themselves as security modules When a security module is loaded, it must register itself with the LSM framework by calling the "register-security" function. This function sets the global "security_ops table" to refer to the module’s hook function pointers(即初始状态下,"security-ops"是指向NULL的指针), causing the kernel to call into the security module for access control decisions. The "register-security" function will not overwrite a previously loaded module. Once a security module is loaded, it becomes a policy decision whether it will allow itself to be unloaded. 5. the patch moves most of the capabilities logic into an optional security module, The Linux kernel currently provides support for a subset of POSIX.1e capabilities.
0x4: LSM Hook Method
1. Task Hooks LSM provides a set of task hooks that enable security modules to manage process security information and to control process operations 2. Program Loading Hooks LSM provides a set of programloading hooks that are called at critical points during the processing of an execve operation.
"linux_binprm" 3. IPC Hooks Security modules can manage security information and perform access control for System V IPC using the LSM IPC hooks. LSM inserts a hook into the existing ipcperms function so that a security module can perform a check for each existing Linux IPC permission check 4. Filesystem Hooks For file operations, three sets of hooks were defined: 1) filesystem hooks 2) inode hooks 3) file hooks 5. Network Hooks Application layer access to networking is mediated using a set of socket hooks. These hooks, which include interposition of all socket system calls, provide coarse mediation coverage of all socket-based protocols. Since active user sockets have an associated inode structure, a separate security field was not added to the socket structure or to the lower-level sock structure. As the socket hooks allow general mediation of network traffic in relation to processes, LSM significantly expands the kernel’s network access control framework (which is already handled at the network layer by Netfilter)(LSM对网络的访问控制和Netfilter保持兼容). For example, the sock rcv skb hook allows an inbound packet to be mediated in terms of its destination application, prior to being queued at the associated userspace socket. 6. Other Hooks LSM provides two additional sets of hooks: 1) module hooks Module hooks can be used to control the kernel operations that create, initialize, and delete kernel modules. 2) a set of top-level system hooks System hooks can be used to control system operations, such as setting the system hostname, accessing I/O ports, and configuring process accounting.
可以看出,LSM在向上层提供了一个统一的Hook的接口的同时,在下层进行了大量的兼容,对于Linux系统的各个子系统来说,要实现对它们的Hook,差异性是不言而喻的,LSM的Hook Point就像触手一样遍布在Linux Kernel的各个角落
0x5: 基于LSM框架的具体实现
LSM provides only the mechanism to enforce enhanced access control policies. Thus, it is the LSM modules that implement a specific policy and are critical in proving the functionality of the framework.
1. SELinux http://en.wikipedia.org/wiki/Security-Enhanced_Linux http://selinuxproject.org/page/NB_LSM A Linux implementation of the Flask flexible access control architecture and an example security server that supports Type Enforcement, Role-Based Access Control, and optionally MultiLevel Security. SELinux was originally implemented as a kernel patch and was then reimplemented as a security module that uses LSM. SELinux can be used to confine processes to least privilege, to protect the integrity and confidentiality of processes and data, and to support application security needs. 2. DTE Linux An implementation of Domain and Type Enforcement developed for Linux Like SELinux, DTE Linux was originally implemented as a kernel patch and was then adapted to LSM. With this module loaded, types can be assigned to objects and domains to processes. The DTE policy restricts access between domains and from domains to types. The DTE Linux project also provided useful input into the design and implementation of LSM. 3. LSM port of Openwall kernel patch 4. POSIX.1e capabilities
Relevant Link:
http://lxr.free-electrons.com/source/Documentation/security/LSM.txt https://www.usenix.org/legacy/event/sec02/wright.html http://en.wikipedia.org/wiki/Linux_Security_Modules https://www.usenix.org/legacy/event/sec02/wright.html https://www.usenix.org/legacy/event/sec02/full_papers/wright/wright.pdf https://www.kernel.org/doc/ols/2002/ols2002-pages-604-617.pdf http://se7so.blogspot.com/2012/04/linux-security-modules-framework-lsm.html http://www.kroah.com/linux/talks/ols_2002_lsm_paper/lsm.pdf
2. LSM Sourcecode Analysis
0x1: LSM的数据结构定义
/source/security/security.c
... static struct security_operations *security_ops; static struct security_operations default_security_ops = { .name = "default", }; ... /** * security_init - initializes the security framework * * This should be called early in the kernel initialization sequence. */ int __init security_init(void) { printk(KERN_INFO "Security Framework initialized\n"); security_fixup_ops(&default_security_ops); security_ops = &default_security_ops; do_security_initcalls(); return 0; } .. static void __init do_security_initcalls(void) { initcall_t *call; call = __security_initcall_start; while (call < __security_initcall_end) { (*call) (); call++; } }
内核代码声明了全局静态结构体变量: security_ops,并将结构体初始化为默认Hook结构体(default_security_ops)
LSM中最重要的数据结构,保存所有LSM Hook Point Function的: struct security_operations的结构定义如下
/source/include/linux/security.h
struct security_operations { //A string that acts as a unique identifier for the LSM char name[SECURITY_NAME_MAX + 1]; int (*ptrace_access_check) (struct task_struct *child, unsigned int mode); int (*ptrace_traceme) (struct task_struct *parent); int (*capget) (struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); int (*capset) (struct cred *new, const struct cred *old, const kernel_cap_t *effective, const kernel_cap_t *inheritable, const kernel_cap_t *permitted); int (*capable) (const struct cred *cred, struct user_namespace *ns, int cap, int audit); int (*quotactl) (int cmds, int type, int id, struct super_block *sb); int (*quota_on) (struct dentry *dentry); int (*syslog) (int type); int (*settime) (const struct timespec *ts, const struct timezone *tz); int (*vm_enough_memory) (struct mm_struct *mm, long pages); /* 1. Security hooks for program execution operations 程序装载钩子,LSM(Linux Security Modules)提供了一系列程序装载钩子,用在一个execve()操作执行过程的关键点上 1) "linux_binprm"结构中的安全域允许安全模块维护程序装载过程中的安全信息 2) LSM提供了钩子用于允许安全模块在装载程序前初始化安全信息和执行访问控制 3) LSM提供了钩子允许模块在新程序成功装载后更新任务的安全信息 4) LSM提供了钩子用来控制程序执行过程中的状态继承,例如确认打开的文件描述符(不是可信启动) */ //Save security information in the bprm->security field, typically based on information about the bprm->file, for later use by the apply_creds hook int (*bprm_set_creds) (struct linux_binprm *bprm); //This hook mediates the point when a search for a binary handler will begin. This hook may be called multiple times during a single execve; and in each pass set_creds is called first. int (*bprm_check_security) (struct linux_binprm *bprm); int (*bprm_secureexec) (struct linux_binprm *bprm); //Prepare to install the new security attributes of a process being transformed by an execve operation, based on the old credentials pointed to by @current->cred and the information set in @bprm->cred by the bprm_set_creds hook void (*bprm_committing_creds) (struct linux_binprm *bprm); //Tidy up after the installation of the new security attributes of a process being transformed by an execve operation. void (*bprm_committed_creds) (struct linux_binprm *bprm); /* 2. Security hooks for filesystem operations. */ //Allocate and attach a security structure to the sb->s_security field. int (*sb_alloc_security) (struct super_block *sb); //Deallocate and clear the sb->s_security field. void (*sb_free_security) (struct super_block *sb); //Allow mount option data to be copied prior to parsing by the filesystem, so that the security module can extract security-specific mount options cleanly (a filesystem may modify the data e.g. with strsep()). int (*sb_copy_data) (char *orig, char *copy); //Extracts security system specific mount options and verifies no changes are being made to those options. int (*sb_remount) (struct super_block *sb, void *data); int (*sb_kern_mount) (struct super_block *sb, int flags, void *data); int (*sb_show_options) (struct seq_file *m, struct super_block *sb); //Check permission before obtaining filesystem statistics for the @mnt mountpoint. int (*sb_statfs) (struct dentry *dentry); //Check permission before an object specified by @dev_name is mounted on the mount point named by @nd. int (*sb_mount) (const char *dev_name, struct path *path, const char *type, unsigned long flags, void *data); //Check permission before the @mnt file system is unmounted. int (*sb_umount) (struct vfsmount *mnt, int flags); //Check permission before pivoting the root filesystem. int (*sb_pivotroot) (struct path *old_path, struct path *new_path); //Set the security relevant mount options used for a superblock int (*sb_set_mnt_opts) (struct super_block *sb, struct security_mnt_opts *opts, unsigned long kern_flags, unsigned long *set_kern_flags); //Copy all security options from a given superblock to another int (*sb_clone_mnt_opts) (const struct super_block *oldsb, struct super_block *newsb); //Parse a string of security data filling in the opts structure int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts); //Compute a context for a dentry as the inode is not yet available since NFSv4 has no label backed by an EA anyway. int (*dentry_init_security) (struct dentry *dentry, int mode, struct qstr *name, void **ctx, u32 *ctxlen); #ifdef CONFIG_SECURITY_PATH int (*path_unlink) (struct path *dir, struct dentry *dentry); int (*path_mkdir) (struct path *dir, struct dentry *dentry, umode_t mode); int (*path_rmdir) (struct path *dir, struct dentry *dentry); int (*path_mknod) (struct path *dir, struct dentry *dentry, umode_t mode, unsigned int dev); int (*path_truncate) (struct path *path); int (*path_symlink) (struct path *dir, struct dentry *dentry, const char *old_name); int (*path_link) (struct dentry *old_dentry, struct path *new_dir, struct dentry *new_dentry); int (*path_rename) (struct path *old_dir, struct dentry *old_dentry, struct path *new_dir, struct dentry *new_dentry); int (*path_chmod) (struct path *path, umode_t mode); int (*path_chown) (struct path *path, kuid_t uid, kgid_t gid); int (*path_chroot) (struct path *path); #endif /* 3. Security hooks for inode operations. */ //Allocate and attach a security structure to @inode->i_security. int (*inode_alloc_security) (struct inode *inode); void (*inode_free_security) (struct inode *inode); //Obtain the security attribute name suffix and value to set on a newly created inode and set up the incore security field for the new inode. int (*inode_init_security) (struct inode *inode, struct inode *dir, const struct qstr *qstr, const char **name, void **value, size_t *len); int (*inode_create) (struct inode *dir, struct dentry *dentry, umode_t mode); int (*inode_link) (struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry); int (*inode_unlink) (struct inode *dir, struct dentry *dentry); int (*inode_symlink) (struct inode *dir, struct dentry *dentry, const char *old_name); int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, umode_t mode); int (*inode_rmdir) (struct inode *dir, struct dentry *dentry); int (*inode_mknod) (struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev); int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry); int (*inode_readlink) (struct dentry *dentry); int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd); int (*inode_permission) (struct inode *inode, int mask); int (*inode_setattr) (struct dentry *dentry, struct iattr *attr); int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry); int (*inode_setxattr) (struct dentry *dentry, const char *name, const void *value, size_t size, int flags); void (*inode_post_setxattr) (struct dentry *dentry, const char *name, const void *value, size_t size, int flags); int (*inode_getxattr) (struct dentry *dentry, const char *name); int (*inode_listxattr) (struct dentry *dentry); int (*inode_removexattr) (struct dentry *dentry, const char *name); int (*inode_need_killpriv) (struct dentry *dentry); int (*inode_killpriv) (struct dentry *dentry); int (*inode_getsecurity) (const struct inode *inode, const char *name, void **buffer, bool alloc); int (*inode_setsecurity) (struct inode *inode, const char *name, const void *value, size_t size, int flags); int (*inode_listsecurity) (struct inode *inode, char *buffer, size_t buffer_size); void (*inode_getsecid) (const struct inode *inode, u32 *secid); /* 4. Security hooks for file operations */ //Check file permissions before accessing an open file. This hook is called by various operations that read or write files. int (*file_permission) (struct file *file, int mask); //Allocate and attach a security structure to the file->f_security field int (*file_alloc_security) (struct file *file); //Deallocate and free any security structures stored in file->f_security. void (*file_free_security) (struct file *file); //Check permission for an ioctl operation on @file int (*file_ioctl) (struct file *file, unsigned int cmd, unsigned long arg); //Check permissions for a mmap operation at @addr. int (*mmap_addr) (unsigned long addr); //Check permissions for a mmap operation.