security_operations
structure, which can be found in the include / linux / security.h file: struct security_operations { char name[SECURITY_NAME_MAX + 1]; ... int inode_permission(struct inode * inode, int mask); ... }
static struct security_o perations * security_ops; static struct security_o perations default_security_ops = { . name = "default", };
security_ops
in the corresponding hook : int security_inode_permission(struct inode *inode, int mask) { if (unlikely(IS_PRIVATE(inode))) return 0; return security_ops->inode_permission(inode, mask); }
inode_permission
handler inode_permission
is inode_permission
indirectly using the value of the security_ops
pointer, which corresponds to what was said. A good example would be the illustration of the implementation of access control using the example of inode_permission , a key function of the virtual file system (VFS) of the kernel that controls the access of the current process (subject) to the file system object: int inode_permission(struct inode *inode, int mask) { int retval; retval = sb_permission(inode->i_sb, inode, mask); if (retval) return retval; return __inode_permission(inode, mask); } int __inode_permission(struct inode *inode, int mask) { int retval; if (unlikely(mask & MAY_WRITE)) { /* * Nobody gets write access to an immutable file. */ if (IS_IMMUTABLE(inode)) return -EACCES; } retval = do_inode_permission(inode, mask); if (retval) return retval; retval = devcgroup_inode_permission(inode, mask); if (retval) return retval; return security_inode_permission(inode, mask); }
security_inode_permission
terminates a function and is the last frontier at the stage of verifying the accessibility of a process (subject) to the file (object) represented by the inode
structure of the file system. Thus, it will only work if all the previous checks pass successfully, which corresponds to the LSM concept, in which the security module does not replace the basic system model, but only complements and expands it.v2.6.11
accounted for 134 hooks, then the kernel version 3.8 accounted for 189, which is an indicator of the degree of LSM integration into the kernel. Secondly, the dynamics of changes in the number of hooks indicates the rapid development of LSM to version v2.6.34
and some stabilization after, which is an indicator of the subsystem maturity, its relative completeness and architectural stability.CONFIG_SECURITY
configuration variable and is set at the compilation stage. In practice, this means that the core of the target system can be assembled without LSM. However, in other cases, the use of this possibility for embedding seems to be sufficiently practical, but it has its own characteristics, primarily related to the architecture of LSM itself. Indeed, embedding capabilities are determined by the completeness of the LSM-hooks of the kernel code. In other words, it is possible to be built in only in those places in which it is provided .security_ops
pointer, which is a kernel memory cell that stores the address of the descriptor structure of the current (active) LSM model. At the same time, due to the fact that all operations associated with accessing the model are carried out through this pointer, it can be argued that for embedding it is necessary and sufficient to have the ability to control this value.v2.6.24
, security_ops
not exported and is marked as static
(see commit 189b3b1c89761054fee3438f063d7f257306e2d8 );__init
, which means unloading the memory area containing its code after execution (see commit c1e992b99603a84d7debb188542b64f2d9232c07 );v2.6.34
kernel, v2.6.34
no regular possibility of changing the current security model during the system operation. However, this circumstance can be overcome if a way to determine the address of the current model pointer is found - security_ops
. One of the simple and reliable ways to determine it is disassembling the kernel code, namely, one of the functions where this pointer is used. Indeed, it is worth considering as an example the listing of the security_sb_copy_data export function hook, obtained for a 64-bit x86 system using the gdb
debugger: int security_sb_copy_data(char *orig, char *copy) { return security_ops->sb_copy_data(orig, copy); } EXPORT_SYMBOL(security_sb_copy_data);
# gdb vmlinux /proc/kcore (gdb) x/7i security_sb_copy_data 0xffffffff811f61b0: push %rbp 0xffffffff811f61b1: mov %rsp,%rbp 0xffffffff811f61b4: data32 data32 data32 xchg %ax,%ax 0xffffffff811f61b9: mov 0x881690(%rip),%rax # 0xffffffff81a77850 0xffffffff811f61c0: callq *0x98(%rax) 0xffffffff811f61c6: pop %rbp 0xffffffff811f61c7: retq (gdb) x/s* 0xffffffff81a77850 0xffffffff81850fa0: "default"
callq *0x98(%rax)
calls the handler function sb_copy_data
at offset 0x98
, relative to the pointer stored in %rax
. Namely %rax
and contains security_ops
. Thus, in order to obtain this pointer, it is necessary, sequentially, instruction by instruction, to scan commands from the beginning of this function until finding the command mov 0x881690(%rip),%rax
, loading the desired value into %rax
and, having performed a simple analysis of its operands , get the required pointer. Below is the code that allows this for x86 using the udis86 disassembler: static void * get_lsm_entry(void) { /* this one is exported */ return (void *)&security_sb_copy_data; } static struct security_operations ** get_lsm_sop(void) { ud_t ud; void * entry = get_lsm_entry(), * result = NULL; ud_initialize(&ud, BITS_PER_LONG, UD_VENDOR_ANY, entry, 128); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Imov && \ ud.operand[0].type == UD_OP_REG && ud.operand[1].type == UD_OP_MEM) { #ifdef CONFIG_X86_64 result = entry + ud_insn_off(&ud) + ud_insn_len(&ud); #endif result = result + ud.operand[1].lval.sdword; break; } } return result; }
security_ops
pointer, you can replace the active security model with your own implementation by simply replacing the value of this pointer. As an example, I cite the code for the fakelsm project, which replaces the inode_permission
function.security_ops
!Source: https://habr.com/ru/post/196372/
All Articles