当前位置: 首页 > news >正文

Linux 安全 - LSM源码分析

文章目录

  • 前言
  • 一、简介
    • 1.1 DAC 和 MAC
    • 1.2 LSM 调用流程图
  • 二、LSM相关数据结构
    • 2.1 struct security_hook_list
    • 2.2 union security_list_options
    • 2.3 structure security_hook_heads
  • 三、security_bprm_check
  • 四、LSM 源码分析
    • 3.1 early_security_init
    • 3.2 security_init
      • 3.2.1 security_init
      • 3.2.2 ordered_lsm_init
      • 3.2.3 initialize_lsm
  • 五、LSM初始化例程
    • 5.1 selinux
    • 5.2 apparmor
  • 参考资料

前言

这篇文章介绍了LSM相关知识:Linux 安全 - LSM机制,接下来源码分析LSM模块。

一、简介

1.1 DAC 和 MAC

(1)
DAC(Discretionary Access Control)基于访问控制是一种根据主体或组的身份限制对对象访问的手段。DAC使用用户和组权限,实现访问控制。DAC的一个问题是其基本操作是可传递的。一个特权用户可以创建其他特权用户,并且这些用户可能具有对受限对象的访问权限。

传统的Linux UID/GID机制,Linux通过用户、进程、文件的UID/GID来进行权限管理的。Linux将文件的权限划分为读、写和执行三种,分别用字母r、w和x表示。每一个文件有三组读、写和执行权限,分别是针对文件的所有者(u)、文件所有者所属的组(g)以及除前两种之外的其它用户(o)。这样,如果一个用户想要将一个自己创建的文件交给另外一个用户访问,那么只需要相应地设置一下这个文件的其它用户权限位就可以了。文件的权限控制在所有者手中。

(2)
MAC(Mandatory Access Control)中,主体(例如用户、进程、线程)和对象(例如文件、套接字、内存段)都具有一组安全属性。这些安全属性通过MAC策略进行集中管理。在MAC的情况下,用户/组不做任何访问决策,而是由安全属性进行管理。

每当主体尝试访问对象时,都会由操作系统内核强 制执行授权规则–检查安全属性并决定是否可进行访问。同样,任何主体对任何对象的任何操作都将根据一组授权规则(策略)进行测试,决定操作是否被允许。

MAC提供了更严格的访问控制,它不依赖于用户或组的身份,而是根据预定义的安全策略和属性来进行访问决策。这种策略可能基于许多因素,如用户角色、标签、上下文等。通过使用MAC,可以实现更细粒度的访问控制,并提供更高的安全性,以防止特权用户滥用权限或绕过访问控制规则。

在Linux中,MAC功能由安全模块(Linux Security Module)提供,如SELinux(Security-Enhanced Linux)和AppArmor。这些安全模块通过管理对象的安全上下文和定义访问策略来实现MAC。与DAC相比,MAC提供了更强大的安全能力,特别适用于需要更严格访问控制的环境,如多用户系统、服务器和敏感数据系统。

1.2 LSM 调用流程图

LSM通过在内核代码中恰好在访问内核对象(比如inode)之前放置挂钩来调解对内核对象的访问。
在这里插入图片描述
当用户空间中的进程调用open()系统调用打开文件时,涉及以下步骤:

(1)用户空间中的进程调用open()系统调用,并提供文件路径作为参数。

(2)系统调度该系统调用,并使用提供的文件路径获取与文件关联的内核文件对象和inode对象。

(3)如果open()系统调用的参数不正确或无效,则返回错误。

(4)内核执行常规的"自主访问控制"(DAC)文件权限检查。它验证当前用户是否具有打开文件所需的权限。如果用户没有所需的权限,则终止系统调用,并将错误返回给用户空间。

(5)如果DAC检查通过,并且用户具有所需的权限,则Linux安全模块(LSM)框架开始工作。对于每个已启用的LSM,框架调用file_open钩子函数。每个LSM都有机会执行额外的安全检查或修改文件打开操作的行为。如果任何LSM钩子函数返回错误,则终止系统调用,并将错误返回给用户空间。

(6)最后,如果所有的安全检查,包括DAC和LSM钩子函数,都通过了,文件将被打开给进程。创建一个新的文件描述符,并返回给用户空间的进程,允许对已打开文件进行进一步操作。

当进程调用open()打开文件时,内核首先执行DAC文件权限检查,如果通过,然后调用LSM框架执行额外的安全检查。只有当所有的安全检查都通过后,文件才会被打开,并将文件描述符返回给进程。

从上图可以看出,LSM挂钩是在执行DAC和其他健全性检查之后应用的。

通过系统调用进入内核之后,系统首先进行传统的DAC权限检查(传统权限检查主要是基于用户的,用户通过验证之后就可以访问资源),通过之后才会进行MAC强制访问控制 ,从图上看来,LSM实现MAC强制访问控制主要通过LSM安全模块的钩子函数实现。

LSM是一种基于MAC的控制形式,常见的有SELinux/AppArmor。

以下是selinux LSM 简易图:
在这里插入图片描述
在这里插入图片描述

二、LSM相关数据结构

2.1 struct security_hook_list

struct hlist_node {struct hlist_node *next, **pprev;
};struct hlist_head {struct hlist_node *first;
};union security_list_options {.....int (*ptrace_access_check)(struct task_struct *child,unsigned int mode);......
}/** 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;

security_hook_list结构体用于表示安全模块钩子列表的结构。这个结构体被用于在通用的列表操作中进行常见操作。

结构体的成员含义:
(1)list:struct hlist_node类型的成员,用于将security_hook_list结构体链接到一个哈希链表中。哈希链表提供了高效的查找和插入操作。

(2)head:指向struct hlist_head类型的指针,指向哈希链表的头部。哈希链表的头部包含指向链表中第一个元素的指针。

(3)hook:union security_list_options类型的联合体,表示安全模块钩子的选项。这是一个通用的选项字段,具体的含义可能取决于具体的安全模块和使用场景。

(4)lsm:一个指向字符(char)类型的指针,用于存储与此安全模块钩子相关的LSM(Linux Security Module)的名称。LSM是Linux内核的安全模块架构,用于实现各种安全策略和功能。

(5)__randomize_layout:一个特殊的属性,用于在内存中随机化这个结构体的布局。这有助于增加系统的安全性,使攻击者更难利用结构体的布局来进行攻击。

这个结构体的设计使得可以使用通用的列表宏(generic list macros)对安全模块钩子进行常见操作,例如插入、删除和遍历。它提供了一种灵活的方式来管理安全模块钩子,并与其他系统组件进行交互。

2.2 union security_list_options

/*** union security_list_options - Linux Security Module hook function list** Security hooks for program execution operations.***/union security_list_options {int (*binder_set_context_mgr)(struct task_struct *mgr);int (*binder_transaction)(struct task_struct *from,struct task_struct *to);int (*binder_transfer_binder)(struct task_struct *from,struct task_struct *to);int (*binder_transfer_file)(struct task_struct *from,struct task_struct *to,struct file *file);int (*ptrace_access_check)(struct task_struct *child,unsigned int mode);int (*ptrace_traceme)(struct task_struct *parent);......
}

为LSM定义的安全挂钩的函数指针的联合,在内核代码中的各种关键路径上调用。

2.3 structure security_hook_heads

struct security_hook_heads {struct hlist_head binder_set_context_mgr;struct hlist_head binder_transaction;struct hlist_head binder_transfer_binder;struct hlist_head binder_transfer_file;struct hlist_head ptrace_access_check;struct hlist_head ptrace_traceme;......

该数据结构包含与每个钩子相对应的链表的头部,考虑到LSM的堆叠属性,从而允许它们以正确的顺序执行。

三、security_bprm_check

SYSCALL_DEFINE5(execveat-->do_execveat()-->do_execveat_common()-->__do_execve_file()--exec_binprm()-->search_binary_handler()-->security_bprm_check()
int security_bprm_check(struct linux_binprm *bprm)
{int ret;ret = call_int_hook(bprm_check_security, 0, bprm);if (ret)return ret;return ima_bprm_check(bprm);
}

函数 security_bprm_check 就是一个LSM HOOK 点,用于在执行二进制文件之前进行安全性检查。

struct security_hook_heads security_hook_heads __lsm_ro_after_init;
#define call_int_hook(FUNC, IRC, ...) ({			\int RC = IRC;						\do {							\struct security_hook_list *P;			\\hlist_for_each_entry(P, &security_hook_heads.FUNC, list) { \RC = P->hook.FUNC(__VA_ARGS__);		\if (RC != 0)				\break;				\}						\} while (0);						\RC;							\
})

宏 call_int_hook,用于调用安全模块的钩子函数。

这个宏的作用是依次调用安全模块的钩子函数,并将它们的返回值进行处理。

宏接受三个参数:
FUNC:安全模块钩子函数的名称。这个宏将通过 FUNC 参数拼接出对应的钩子函数列表的成员。
IRC:初始返回值(Initial Return Code)。这个值将被用作初始的返回值,如果所有的钩子函数都返回0,则最终的返回值将是初始返回值。
…:可变参数,用于传递给钩子函数的参数。

宏的工作流程如下:
使用 hlist_for_each_entry 宏遍历存储在 security_hook_heads.FUNC 中的钩子函数列表。
对于每个钩子函数,调用 P->hook.FUNC 并传递可变参数 __VA_ARGS__。
如果钩子函数的返回值 RC 不等于0,则跳出循环。

四、LSM 源码分析

start_kernel()-->early_security_init()......-->security_init()/* Load LSMs in specified order. */-->ordered_lsm_init()-->initialize_lsm()-->lsm->init()-->selinux_init()-->apparmor_init()-->tomoyo_init()-->smack_init()

由于selinux、apparmor、tomoyo和smack都是LSM_FLAG_EXCLUSIVE 独占模式标志,因此只能选择其中的一个启动。

3.1 early_security_init

struct security_hook_heads security_hook_heads __lsm_ro_after_init;int __init early_security_init(void)
{int i;struct hlist_head *list = (struct hlist_head *) &security_hook_heads;struct lsm_info *lsm;for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct hlist_head);i++)INIT_HLIST_HEAD(&list[i]);for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {if (!lsm->enabled)lsm->enabled = &lsm_enabled_true;prepare_lsm(lsm);initialize_lsm(lsm);}return 0;
}

early_security_init函数是在系统初始化过程中早期调用的安全初始化函数。

(1)将security_hook_heads的地址强制转换为struct hlist_head指针,并将其赋值给名为list的变量。security_hook_heads是一个全局变量,它存储了一组hlist_head结构体,用于存储不同安全钩子的链表头部。

(2)循环遍历security_hook_heads数组,计算需要迭代的次数。通过sizeof(security_hook_heads)除以sizeof(struct hlist_head),可以确定数组中有多少个hlist_head元素。

(3)INIT_HLIST_HEAD(&list[i]);:对list[i]所指向的hlist_head进行初始化,将其设置为空链表。

(4)循环遍历__start_early_lsm_info到__end_early_lsm_info之间的lsm_info结构体数组,这些结构体包含了早期加载的安全模块信息。

(5)调用prepare_lsm函数,对当前安全模块进行准备工作。

/* Prepare LSM for initialization. */
static void __init prepare_lsm(struct lsm_info *lsm)
{int enabled = lsm_allowed(lsm);/* Record enablement (to handle any following exclusive LSMs). */set_enabled(lsm, enabled);/* If enabled, do pre-initialization work. */if (enabled) {if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {exclusive = lsm;init_debug("exclusive chosen: %s\n", lsm->name);}lsm_set_blob_sizes(lsm->blobs);}
}

lsm_allowed(lsm):该函数判断给定的 LSM 是否被允许启用。
set_enabled(lsm, enabled):该函数记录 LSM 的启用状态,以便处理后续的互斥 LSM。
如果 LSM 被启用:
检查是否为互斥 LSM(lsm->flags & LSM_FLAG_EXCLUSIVE)并且当前没有已选择的互斥 LSM(!exclusive),如果是,则将该 LSM 设置为当前互斥 LSM(exclusive = lsm)。
调用 lsm_set_blob_sizes(lsm->blobs) 设置该 LSM 的相关数据块大小。

prepare_lsm函数通过调用lsm_allowed函数进行判断,如果有模块的flag中加入了LSM_FLAG_EXCLUSIVE,则注册成独占模块,其他声明了LSM_FLAG_EXCLUSIVE的模块就不会被启用。常见Linux系统中lockdown,yama,loadpin,safesetid,integrity以及capbility均为非独占模块,在内核中可以同时加载;而selinux,apparmor以及smack均是独占模块,不能同时加载。

(6)调用initialize_lsm函数,对当前安全模块进行初始化。

/* Initialize a given LSM, if it is enabled. */
static void __init initialize_lsm(struct lsm_info *lsm)
{if (is_enabled(lsm)) {int ret;init_debug("initializing %s\n", lsm->name);ret = lsm->init();WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);}
}

如果给定的 LSM 已启用,调用 lsm->init() 初始化该 LSM。

总的来说,这段代码的作用是在系统初始化早期对安全模块进行初始化。它遍历安全钩子的链表头部,将每个链表头部初始化为空链表。然后,对早期加载的安全模块进行准备和初始化操作,确保它们的状态正确并可用于后续的安全处理。

3.2 security_init

3.2.1 security_init

LSM内核调用security_init进行LSM框架初始化,该初始化按以下顺序加载已启用的受支持的Linux安全模块:

– Capability module
– Minor LSMs
– Major LSM
// linux-5.4.18/include/asm-generic/vmlinux.lds.h#ifdef CONFIG_SECURITY
#define LSM_TABLE()	. = ALIGN(8);					\__start_lsm_info = .;				\KEEP(*(.lsm_info.init))				\__end_lsm_info = .;
#define EARLY_LSM_TABLE()	. = ALIGN(8);				\__start_early_lsm_info = .;			\KEEP(*(.early_lsm_info.init))			\__end_early_lsm_info = .;

vmlinux.lds.h 是 Linux 内核源代码中的一个文件,用于定义链接器脚本(Linker Script)来控制内核镜像的链接和布局。

LSM_TABLE() 和 EARLY_LSM_TABLE() 宏被用于定义与 Linux 安全模块(LSM)相关的信息表。这些宏定义了链接器脚本中的一系列指令,用于在生成的内核镜像中定位和保留 LSM 相关的初始化数据。

这些宏执行以下操作:
(1)
LSM_TABLE() 宏:

. = ALIGN(8);:将当前位置对齐到下一个 8 字节边界。
__start_lsm_info = .;:设置 __start_lsm_info 符号为当前位置。
KEEP(*(.lsm_info.init)):保留所有 .lsm_info.init 节的内容。
__end_lsm_info = .;:设置 __end_lsm_info 符号为当前位置。

(2)
EARLY_LSM_TABLE() 宏:

. = ALIGN(8);:将当前位置对齐到下一个 8 字节边界。
__start_early_lsm_info = .;:设置 __start_early_lsm_info 符号为当前位置。
KEEP(*(.early_lsm_info.init)):保留所有 .early_lsm_info.init 节的内容。
__end_early_lsm_info = .;:设置 __end_early_lsm_info 符号为当前位置。

这些宏的目的是在链接器脚本中定义特定的位置和符号,以便将与 LSM 相关的初始化数据放置在正确的位置,并在内核镜像中保留这些数据。

// linux-5.4.18/include/linux/lsm_hooks.h/** Security blob size or offset data.*/
struct lsm_blob_sizes {int	lbs_cred;int	lbs_file;int	lbs_inode;int	lbs_ipc;int	lbs_msg_msg;int	lbs_task;
};enum lsm_order {LSM_ORDER_FIRST = -1,	/* This is only for capabilities. */LSM_ORDER_MUTABLE = 0,
};struct lsm_info {const char *name;	/* Required. */enum lsm_order order;	/* Optional: default is LSM_ORDER_MUTABLE */unsigned long flags;	/* Optional: flags describing LSM */int *enabled;		/* Optional: controlled by CONFIG_LSM */int (*init)(void);	/* Required. */struct lsm_blob_sizes *blobs; /* Optional: for blob sharing. */
};/*** security_init - initializes the security framework** This should be called early in the kernel initialization sequence.*/
int __init security_init(void)
{struct lsm_info *lsm;pr_info("Security Framework initializing\n");/** Append the names of the early LSM modules now that kmalloc() is* available*/for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {if (lsm->enabled)lsm_append(lsm->name, &lsm_names);}/* Load LSMs in specified order. */ordered_lsm_init();return 0;
}

security_init函数用于初始化安全框架。它应该在内核初始化序列的早期调用。

(1)定义一个指向lsm_info结构体的指针变量lsm,用于遍历LSM模块的数组。循环遍历__start_early_lsm_info到__end_early_lsm_info之间的lsm_info结构体数组,这些结构体包含了早期加载的安全模块的信息。

(2)调用ordered_lsm_init函数,按照指定的顺序加载LSM模块。

这段代码的作用是初始化安全框架。它遍历早期加载的安全模块数组,将启用的模块名称追加到链表中。然后,按照指定的顺序加载LSM模块。这个函数在内核初始化的早期阶段被调用,用于确保安全框架在系统启动时得到正确的初始化。

3.2.2 ordered_lsm_init

/* Boot-time LSM user choice */
static __initdata const char *chosen_lsm_order;
static __initdata const char *chosen_major_lsm;static __initconst const char * const builtin_lsm_order = CONFIG_LSM;/* Ordered list of LSMs to initialize. */
static __initdata struct lsm_info **ordered_lsms;static void __init ordered_lsm_init(void)
{struct lsm_info **lsm;ordered_lsms = kcalloc(LSM_COUNT + 1, sizeof(*ordered_lsms),GFP_KERNEL);if (chosen_lsm_order) {if (chosen_major_lsm) {pr_info("security= is ignored because it is superseded by lsm=\n");chosen_major_lsm = NULL;}ordered_lsm_parse(chosen_lsm_order, "cmdline");} elseordered_lsm_parse(builtin_lsm_order, "builtin");for (lsm = ordered_lsms; *lsm; lsm++)prepare_lsm(*lsm);init_debug("cred blob size     = %d\n", blob_sizes.lbs_cred);init_debug("file blob size     = %d\n", blob_sizes.lbs_file);init_debug("inode blob size    = %d\n", blob_sizes.lbs_inode);init_debug("ipc blob size      = %d\n", blob_sizes.lbs_ipc);init_debug("msg_msg blob size  = %d\n", blob_sizes.lbs_msg_msg);init_debug("task blob size     = %d\n", blob_sizes.lbs_task);/** Create any kmem_caches needed for blobs*/if (blob_sizes.lbs_file)lsm_file_cache = kmem_cache_create("lsm_file_cache",blob_sizes.lbs_file, 0,SLAB_PANIC, NULL);if (blob_sizes.lbs_inode)lsm_inode_cache = kmem_cache_create("lsm_inode_cache",blob_sizes.lbs_inode, 0,SLAB_PANIC, NULL);lsm_early_cred((struct cred *) current->cred);lsm_early_task(current);for (lsm = ordered_lsms; *lsm; lsm++)initialize_lsm(*lsm);kfree(ordered_lsms);
}

ordered_lsm_init函数用于按照指定的顺序初始化LSM模块。

代码含义如下:
(1)定义一个指向lsm_info结构体指针的指针变量lsm,用于遍历LSM模块的指针数组。
(2)使用kcalloc函数为ordered_lsms分配内存,大小为LSM_COUNT + 1个指针的大小。这个数组用于存储按顺序加载的LSM模块的指针。
(3)如果存在chosen_lsm_order,表示在内核命令行中指定了LSM的加载顺序。调用ordered_lsm_parse函数解析chosen_lsm_order,解析类型为"cmdline",将解析结果存储在ordered_lsms数组中。
(4)如果没有指定chosen_lsm_order,则使用内核编译时的默认LSM加载顺序。调用ordered_lsm_parse函数解析builtin_lsm_order,解析类型为"builtin",将解析结果存储在ordered_lsms数组中。
(5)遍历ordered_lsms数组,对于每个非空指针,调用prepare_lsm函数进行LSM的准备工作。
(6)打印LSM需要的blob大小的调试信息,包括cred、file、inode、ipc、msg_msg和task blob的大小。
(7)如果blob_sizes.lbs_file不为0,使用kmem_cache_create函数创建一个名为lsm_file_cache的内存缓存,用于存储file blob。
(8)如果blob_sizes.lbs_inode不为0,使用kmem_cache_create函数创建一个名为lsm_inode_cache的内存缓存,用于存储inode blob。
(9)调用lsm_early_cred函数,传递当前进程的cred结构作为参数,进行早期的cred blob初始化。
(10)调用lsm_early_task函数,传递当前进程作为参数,进行早期的task blob初始化。
(11)再次遍历ordered_lsms数组,对于每个非空指针,调用initialize_lsm函数进行LSM的初始化。

这段代码的作用是按照指定的顺序初始化LSM模块。它根据指定的LSM加载顺序解析LSM模块,并进行准备工作和初始化。同时,它还创建了用于存储file blob和inode blob的内存缓存。在LSM初始化之前,它对cred blob和task blob进行了早期的初始化。完成初始化后,释放了用于存储LSM模块指针的内存。

3.2.3 initialize_lsm

/* Initialize a given LSM, if it is enabled. */
static void __init initialize_lsm(struct lsm_info *lsm)
{if (is_enabled(lsm)) {int ret;init_debug("initializing %s\n", lsm->name);ret = lsm->init();WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);}
}

lsm->init()表示调用给定LSM的初始化函数。

这个函数用于初始化给定的LSM,前提是该LSM是启用的。它首先检查LSM是否启用,然后调用LSM的初始化函数:

selinux_init()
apparmor_init()
tomoyo_init()
smack_init()

五、LSM初始化例程

5.1 selinux

/* SELinux requires early initialization in order to labelall processes and objects when they are created. */
DEFINE_LSM(selinux) = {.name = "selinux",.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,.enabled = &selinux_enabled,.blobs = &selinux_blob_sizes,.init = selinux_init,
};

这段代码的作用是定义了一个名为selinux的LSM模块的信息结构体,并初始化了结构体的成员。

(1).name = “selinux”:设置了name成员,表示LSM模块的名称是"selinux"。

(2).flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE:设置了flags成员,表示LSM模块的标志。LSM_FLAG_LEGACY_MAJOR标志表示该模块是遗留的主要模块,LSM_FLAG_EXCLUSIVE标志表示该模块是独占的,不与其他模块共存。比如 selinux 不能和 apparmor 同时存在。

(3).enabled = &selinux_enabled:设置了enabled成员,指向一个名为selinux_enabled的变量。这个变量用于指示selinux模块是否启用。

(4).blobs = &selinux_blob_sizes:设置了blobs成员,指向一个名为selinux_blob_sizes的变量。这个变量用于指定selinux模块需要的各个blob的大小。

(5).init = selinux_init:设置了init成员,指向一个名为selinux_init的函数。这个函数用于初始化selinux模块。

通过DEFINE_LSM宏创建的结构体被赋值给selinux,从而定义了一个名为selinux的LSM模块的信息结构体。这个结构体包含了模块的名称、标志、启用状态、blob大小和初始化函数等信息。

DEFINE_LSM宏:

#define DEFINE_LSM(lsm)							\static struct lsm_info __lsm_##lsm				\__used __section(.lsm_info.init)			\__aligned(sizeof(unsigned long))

宏定义用于定义LSM模块的信息结构体,这个宏定义包含了以下几个部分:
(1)DEFINE_LSM(lsm):定义了一个名为DEFINE_LSM的宏,宏的参数是lsm,用于指定LSM模块的名称。

(2)static struct lsm_info _lsm##lsm:定义了一个名为__lsm_加上lsm参数的结构体,该结构体用于存储LSM模块的信息。使用static关键字使结构体具有文件作用域。

(3)__used:这是一个编译器特定的属性,用于告诉编译器即使没有使用到这个结构体,也不要优化掉它。

(4)__section(.lsm_info.init):这是一个编译器特定的属性,用于指定将这个结构体放置在名为.lsm_info.init的特定节(section)中。这个节可能是用于存放LSM模块信息的特定节。

(5)__aligned(sizeof(unsigned long)):这是一个编译器特定的属性,用于指定结构体的对齐方式。在这里,结构体的对齐方式被设置为unsigned long的大小。

这个宏定义用于创建一个静态的LSM信息结构体,该结构体存储了LSM模块的相关信息,并通过编译器的属性设置将它放置在特定的节中。这个宏定义通常用于在LSM模块的源文件中定义LSM信息结构体。

static __init int selinux_init(void)
{security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
}
/*** security_add_hooks - Add a modules hooks to the hook lists.* @hooks: the hooks to add* @count: the number of hooks to add* @lsm: the name of the security module** Each LSM has to register its hooks with the infrastructure.*/
void __init security_add_hooks(struct security_hook_list *hooks, int count,char *lsm)
{int i;for (i = 0; i < count; i++) {hooks[i].lsm = lsm;hlist_add_tail_rcu(&hooks[i].list, hooks[i].head);}......
}
/** 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;
static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),LSM_HOOK_INIT(binder_transfer_binder, selinux_binder_transfer_binder),LSM_HOOK_INIT(binder_transfer_file, selinux_binder_transfer_file),LSM_HOOK_INIT(ptrace_access_check, selinux_ptrace_access_check),LSM_HOOK_INIT(ptrace_traceme, selinux_ptrace_traceme),......
}
/** 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 } }

(1)LSM_HOOK_INIT(HEAD, HOOK):定义了一个名为LSM_HOOK_INIT的宏,宏的参数是HEAD和HOOK,用于指定security_hook_list结构体的成员。

(2){ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }:这是一个结构体初始化的语法,用于初始化security_hook_list结构体的成员。具体来说:

.head = &security_hook_heads.HEAD:初始化head成员,将其指向security_hook_heads结构体中的HEAD成员的地址。security_hook_heads是一个全局变量,用于存储各个hook链表的头部。

struct security_hook_heads {.....struct hlist_head ptrace_access_check;......
}

.hook = { .HEAD = HOOK }:初始化hook成员,其中的.HEAD = HOOK将HOOK的值赋给hook中对应的HEAD成员。这里的HEAD是一个宏参数,用于指定具体的hook函数

union security_list_options {......int (*ptrace_access_check)(struct task_struct *child,unsigned int mode);......
}

struct security_hook_heads这个全局变量用于存储各个hook链表的头部。

比如:

LSM_HOOK_INIT(ptrace_access_check, selinux_ptrace_access_check)

等价于:

{ .head = &security_hook_heads.ptrace_access_check, .hook = { .ptrace_access_check= selinux_ptrace_access_check} }

这个宏定义用于简化初始化security_hook_list结构体的操作。通过宏调用,可以用较少的代码初始化security_hook_list结构体中的head和hook成员。

如果Linux使用的是 selinux,那么执行 exec系统调用时,执行 security_bprm_check hook点就是执行 selinux_ptrace_access_check 函数。

5.2 apparmor

apparmor 和 selinux 初始化过程相同:

DEFINE_LSM(apparmor) = {.name = "apparmor",.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,.enabled = &apparmor_enabled,.blobs = &apparmor_blob_sizes,.init = apparmor_init,
};
static int __init apparmor_init(void)
{......security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),"apparmor");......
}
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),LSM_HOOK_INIT(capget, apparmor_capget),LSM_HOOK_INIT(capable, apparmor_capable),......
}

如果Linux使用的是 apparmor,那么执行 exec系统调用时,执行 security_bprm_check hook点就是执行 apparmor_ptrace_access_check函数。

参考资料

Linux 5.4.18

https://pwl999.blog.csdn.net/article/details/107066666
https://blog.csdn.net/feelabclihu/article/details/121059328
https://github.com/kubearmor/KubeArmor/wiki/Introduction-to-Linux-Security-Modules-(LSMs)

相关文章:

Linux 安全 - LSM源码分析

文章目录 前言一、简介1.1 DAC 和 MAC1.2 LSM 调用流程图 二、LSM相关数据结构2.1 struct security_hook_list2.2 union security_list_options2.3 structure security_hook_heads 三、security_bprm_check四、LSM 源码分析3.1 early_security_init3.2 security_init3.2.1 secu…...

第一次汇报相关问题

深度学习现在已经学习到了Mini-Batch&#xff0c;early-stop等针对特定场景优化的算法了。 代码已经实现了一个L层的神经网络的构建了 论文看了一些综述 主要思考的两个方向&#xff1a;云计算和嵌入式 云计算&#xff1a;分布式机器学习、联邦学习、服务器负载均衡等 嵌入式&…...

[产品体验] GPT4识图功能

[产品体验] GPT4识图功能 图片配文字超强的OCR能力知识问答多图解释 打开chatgpt的时候突然发现能用识图了&#xff0c;赶紧去体验一下&#xff0c;大大的震撼… 图片配文字 超强的OCR能力 我传上去的图片并不清晰… 还能准确识别&#xff0c;orz &#xff01; 知识问答 多…...

《3D 数学基础》几何检测-最近点

目录 1. 直线上的最近点 2. 射线上的最近点 3. 点到平面的距离 4. 圆或球上的最近点 5. AABB上的最近点 1. 直线上的最近点 q是距离q的最近点&#xff0c;也就是q在直线上的投影。 其中p是直线上的点&#xff08;向量表示&#xff09;&#xff0c;n是直线的法向量&#x…...

动态规划 -背包问题-详解

问题 注&#xff1a;大佬对此类问题的解法&#xff1a;动态规划背包问题总结 给你一个由 不同 整数组成的数组 nums &#xff0c;和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。 题目数据保证答案符合 32 位整数范围。 示例 1&#xff…...

Bootstrap-- 媒体特性

最大、最小宽度例子&#xff1a; 横屏与竖屏例子&#xff1a; 宽度比与像素比例子&#xff1a;...

c# 用非递归的写法实现递归

最近写代码碰到了一个bug&#xff0c;就是递归次数太多爆堆栈了&#xff0c;然后就写了一个递归工具来解决这个问题。 using System; using System.Collections.Generic;/// <summary> /// 递归工具 /// </summary> public static class RecursionTool {//递归方式…...

nginx之location的优先级和nginx的重定向

一、nginx之location的优先级和匹配方式&#xff08;重点&#xff09; &#xff08;一&#xff09;nginx的正则表达式 nginx的正则表达式 符号 含义 ^ 字符串的起始位置&#xff08;以什么开头&#xff09; $ 字符串的结束位置&#xff08;以什么结尾&#xff09; * 匹…...

【计算机网络】——前言计算机网络发展的历程概述

主页点击直达&#xff1a;个人主页 我的小仓库&#xff1a;代码仓库 C语言偷着笑&#xff1a;C语言专栏 数据结构挨打小记&#xff1a;初阶数据结构专栏 Linux被操作记&#xff1a;Linux专栏 LeetCode刷题掉发记&#xff1a;LeetCode刷题 算法&#xff1a;算法专栏 C头…...

eventfd

1. #include <sys/eventfd.h> int eventfd(unsigned int initval, int flags); //创建eventfd 参数含义&#xff1a; initval&#xff1a;创建eventfd时它所对应的64位计数器的初始值&#xff1b; flags&#xff1a;eventfd文件描述符的标志&#xff0c;可由三种选项组…...

BES耳机空间音频技术实现

BES耳机空间音频技术实现 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?加我微信hezkz17, 本群提供音频技术答疑服务 音响和耳机在空间音频技术上实现方式是不同的 虚拟现实可谓是空间音频技术最具代表性的应 用领域。虽然虚拟现实的起源可以追溯到1 9 6 8年, …...

day27--AJAX(bootstrap之modal,toast;接口文档的一些用法;AJAX原理)

目录 Bootstrap之Modal&#xff1a; 显示和隐藏方法 通过自定义属性&#xff1a; 使用JS来控制弹框&#xff1a; Bootstrap之Toast&#xff1a; 接口文档一些用法&#xff1a; 删除图书&#xff1a; 图片上传&#xff1a; 图片上传步骤&#xff1a; 修改头像&#xf…...

【ArcGIS Pro二次开发】(70):杂七杂八的记录

本文用于记录一些使用频率较高但归类繁杂&#xff0c;非系统性的一些代码。 主要方便自己使用和查阅&#xff0c;随时更新。 1、从GDB数据库中打开【FeatureDataset\FeatureClass\Table】 using Geodatabase gdb new Geodatabase(new FileGeodatabaseConnectionPath(new Uri…...

竞赛选题 深度学习 机器视觉 人脸识别系统 - opencv python

文章目录 0 前言1 机器学习-人脸识别过程人脸检测人脸对其人脸特征向量化人脸识别 2 深度学习-人脸识别过程人脸检测人脸识别Metric Larning 3 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习 机器视觉 人脸识别系统 该项目…...

【工具】SSH端口转发管理器,专门管理SSH Port Forwarding

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 开源代码看这里&#xff1a;http://xfxuezhang.cn/index.php/archives/1151/ 背景介绍 有时候需要用到ssh的端口转发功能。目前来说&#xff0c;要么是cmd里手敲指令&#xff0c;但每次敲也太麻烦了&#xff1b;或…...

opencv-phase 函数

计算梯度强度和方向 梯度的方向与边缘的方向总是垂直的。图像中的边缘可以指向各个方向&#xff0c;通常会取水平&#xff08;左、右&#xff09;、垂直&#xff08;上、下&#xff09;、对角线&#xff08;左上、右上、左下、右下&#xff09;等八个不同的方向计算梯度。 角度…...

44.ES

一、ES。 &#xff08;1&#xff09;es概念。 &#xff08;1.1&#xff09;什么是es。 &#xff08;1.2&#xff09;es的发展。 es是基于lucene写的。 &#xff08;1.3&#xff09;总结。 es是基于lucene写的。 &#xff08;2&#xff09;倒排索引。 &#xff08;3&#xf…...

分权分域有啥内容?

目前的系统有什么问题&#xff1f; 现在我们的系统越来越庞大&#xff0c;可是每一个人进来的查看到的内容完全一样&#xff0c;没有办法灵活的根据不同用户展示不同的数据 例如我们有一个系统&#xff0c;期望不同权限的用户可以看到不同类型的页面&#xff0c;同一个页面不…...

6.Docker搭建RabbitMQ

1、端口开放 如果在云服务上部署需在安全组开通一下端口&#xff1a;15672、5672、25672、61613、1883。 15672(UI页面通信口)、5672(client端通信口)、25672(server间内部通信口)、61613(stomp 消息传输)、1883(MQTT消息队列遥测传输)。 2、安装镜像 docker pull rabbitmq 3、…...

用 docker 创建 jmeter 容器, 实现性能测试,该如何下手?

用 docker 创建 jmeter 容器, 实现性能测试 我们都知道&#xff0c;jmeter可以做接口测试&#xff0c;也可以用于性能测试&#xff0c;现在企业中性能测试也大多使用jmeter。docker是最近这些年流行起来的容器部署工具&#xff0c;可以创建一个容器&#xff0c;然后把项目放到…...

4年软件测试,突破不了20K,太卷了。。。

先说一个插曲&#xff1a;上个月我有同学在深圳被裁员了&#xff0c;和我一样都是软件测试&#xff0c;不过他是平安外包&#xff0c;所以整个组都撤了&#xff0c;他工资和我差不多都是14K。 现在IT互联网已经比较寒冬&#xff0c;特别是软件测试&#xff0c;裁员先裁测试&am…...

机器人控制算法——两轮差速驱动运动模型

1.Introduction 本文主要介绍针对于两轮差速模型的逆运动学数学推导。因为在机器人控制领域&#xff0c;决策规划控制层给执行器输出的控制指令v(车辆前进速度)和w(角速度)&#xff0c;因此&#xff0c;我们比较关心&#xff0c;当底层两个驱动电机接收到此信息&#xff0c;如何…...

Queue简介

概念&#xff1a; 队列&#xff08;Queue&#xff09;是一种常见的线性数据结构&#xff0c;在Java中用于存储和操作元素序列。它基于先进先出&#xff08;First-In-First-Out, FIFO&#xff09;原则&#xff0c;即最早入队的元素首先出队。只能在队尾添加元素&#xff0c;在队…...

被面试官问到分布式ID,别再傻乎乎只会答雪花算法了...

文章目录 1. 分布式ID2. 数据库主键自增3. 数据库号段模式4. Redis自增5. UUID6. Snowflake (雪花算法)7. Leaf (美团分布式ID生成系统)7.1 Leaf-segment 号段方案7.1.2 双buffer优化 7.2 Leaf-snowflake方案7.3 Leaf-snowflake Demo 1. 分布式ID 在分布式系统中&#xff0c;通…...

使用Boto3访问AWS S3服务

安装Boto3&#xff0c;执行如下命令&#xff1a; python -m venv .venv . .venv/bin/activate python -m pip install boto3创建配置文件&#xff0c;执行如下命令&#xff1a; mkdir -p ~/.aws touch ~/.aws/credentials touch ~/.aws/config编辑 ~/.aws/credentials&#x…...

ODrive移植keil(五)—— 开环控制和电流变换

目录 一、开环控制1.1、控制原理1.2、硬件接线1.3、代码说明1.4、程序演示1.5、程序架构的体现 二、电流变换2.1、理论说明2.2、代码说明 ODrive、VESC和SimpleFOC 教程链接汇总&#xff1a;请点击 一、开环控制 在SimpleFOC系列中有开环控制的教程&#xff0c;SimpleFOC移植S…...

【Java学习之道】日期与时间处理类

引言 在前面的章节中&#xff0c;我们介绍了Java语言的基础知识和核心技能&#xff0c;现在我们将进一步探讨Java中的常用类库和工具。这些工具和类库将帮助我们更高效地进行Java程序开发。在本节中&#xff0c;我们将一起学习日期与时间处理类的使用。 一、为什么需要日期和…...

信息系统项目管理师第四版学习笔记——高级项目管理

项目集管理 项目集管理角色和职责 在项目集管理中涉及的相关角色主要包括&#xff1a;项目集发起人、项目集指导委员会、项目集经理、其他影响项目集的干系人。 项目集发起人和收益人是负责承诺将组织的资源应用于项目集&#xff0c;并致力于使项目集取得成功的人。 项目集…...

MySQL建表操作和用户权限

1.创建数据库school&#xff0c;字符集为utf8 mysql> create database school character set utf8; 2.在school数据库中创建Student和Score表 mysql> create table school.student( -> Id int(10) primary key, -> Stu_id int(10) not null, -> C_n…...

TCP/IP(十一)TCP的连接管理(八)socket网络编程

一 socket网络编程 socket 基本操作函数 bind、listen、connect、accept、recv、send、select、close 说明: 本文需要C语言、syscall系统调用、OS 操作系统基础理论,如果不了解可以暂时跳过目标&#xff1a; 知道对应库函数的更底层机制思考&#xff1a; socket函数与FIN、A…...