Linux内核auditsyscall的机制和原理
Linux内核auditsyscall的机制和原理
Linux 内核中的系统调用审计机制(通常被称为 auditsyscall
)是 Linux Auditing Framework (auditd) 的核心组成部分。它允许系统管理员详细记录特定进程执行了哪些系统调用、何时执行、以什么参数执行,以及调用结果如何。这对于安全审计、入侵检测、故障排查和合规性至关重要。
核心机制与原理:
审计子系统集成:
- Linux 内核内置了一个名为
CONFIG_AUDIT
的编译选项(通常默认启用),它提供了审计的基础设施。 - 审计子系统维护一个内核空间的事件队列(通常基于
sk_buff
结构实现)。 - 用户空间的审计守护进程
auditd
通过NETLINK_AUDIT
套接字(一种特殊的 IPC 机制)连接到内核审计子系统,持续读取队列中的审计事件。
- Linux 内核内置了一个名为
系统调用入口点的挂钩:
- 这是审计发生的最关键点。内核在执行任何系统调用之前(在系统调用分发函数,如
do_syscall_64
或ia32_do_call
中)和系统调用返回之后,插入了特定的审计钩子函数。 - 主要钩子函数:
audit_syscall_entry(int arch, unsigned long syscall, ...)
: 在系统调用实际处理函数执行之前被调用。- 参数:
arch
(体系结构标识符),syscall
(系统调用号),args
(包含系统调用参数的数组,通常最多 6 个参数)。 - 作用:记录“系统调用开始”事件。它捕获即将执行的系统调用的编号和参数。
- 参数:
audit_syscall_exit(long retval)
: 在系统调用实际处理函数执行之后、结果返回给用户空间之前被调用。- 参数:
retval
(系统调用的返回值/错误码)。 - 作用:记录“系统调用结束”事件。它捕获系统调用的执行结果(成功值或错误码)。同时,它会将
entry
和exit
事件关联起来(通常使用当前任务的audit_context
结构)。
- 参数:
- 这是审计发生的最关键点。内核在执行任何系统调用之前(在系统调用分发函数,如
审计上下文:
- 每个进程(任务)在内核中都有一个
task_struct
结构。审计子系统为每个需要审计的进程维护一个audit_context
结构(通常是task_struct->audit_context
)。 - 这个上下文在进程生命周期内(或至少在需要审计的事件期间)存在,用于:
- 临时存储与当前正在审计的事件链相关的信息(例如,关联
entry
和exit
事件)。 - 存储过滤器匹配结果,避免对同一个系统调用多次评估规则。
- 记录额外的审计点信息(如路径名、文件描述符等),这些信息可能在系统调用执行过程中被填充。
- 临时存储与当前正在审计的事件链相关的信息(例如,关联
- 每个进程(任务)在内核中都有一个
审计规则与过滤器:
- 用户空间工具
auditctl
用于配置审计规则。规则定义了哪些事件需要被记录。 - 规则被编译成一种高效的、在内核中运行的 BPF (Berkeley Packet Filter) 字节码。这些规则会被加载到内核审计子系统中。
- 规则匹配过程:
- 当
audit_syscall_entry
被调用时,内核会使用加载的 BPF 过滤器检查当前系统调用(结合进程的凭证uid
,gid
,euid
,egid
,suid
,sgid
,fsuid
,fsgid
,进程IDpid
, 父进程IDppid
, 可执行文件路径exe
, 命令行comm
, 架构arch
, 系统调用号syscall
和参数a0-a5
等上下文信息)是否匹配任何审计规则。 - 如果匹配成功,内核会:
- 在
audit_context
中设置标志,表明这个系统调用需要被审计。 - 记录
entry
事件(包含系统调用号和参数)。
- 在
- 当
audit_syscall_exit
被调用时,它会检查audit_context
中的标志:- 如果标志被设置(表示在
entry
时匹配了规则),则记录exit
事件(包含系统调用号和返回值retval
)。 - 即使
exit
时没有显式规则匹配retval
,只要entry
匹配了,exit
事件就会被记录(因为规则通常关注的是“尝试执行某个动作”)。
- 如果标志被设置(表示在
- 规则也可以基于返回值进行过滤(例如,只记录失败
open
的exit
事件)。
- 当
- 用户空间工具
审计记录的生成与传递:
- 当决定记录一个事件(
SYSCALL
类型的entry
或exit
)时:- 内核分配一个
audit_buffer
。 - 使用
audit_log_format
系列函数将事件信息格式化写入缓冲区。信息包括:- 类型:
SYSCALL
- 时间戳: 精确到纳秒。
- 序列号: 唯一标识事件的递增数字。
- 进程信息:
pid
,ppid
,uid
,gid
,euid
,suid
,fsuid
,egid
,sgid
,fsgid
,auid
(审计用户ID,通常是登录用户的原始UID),ses
(会话ID)。 - 可执行文件信息:
exe
(路径),comm
(命令行)。 - 系统调用信息:
syscall
(编号),arch
(体系结构),success
(是/否,基于exit
的retval
),exit
(具体的返回值/错误码)。 - 参数:
a0
-a5
(对于entry
事件)。 - 其他上下文信息: 如当前工作目录 (
cwd
), 文件路径(如果审计点捕获了,如open
的path
规则项)。
- 类型:
- 格式化的记录被放入内核审计队列。
- 内核分配一个
- 用户空间的
auditd
守护进程持续监控队列。一旦有新记录到达:auditd
通过netlink
套接字读取记录。auditd
根据其配置文件 (auditd.conf
) 将记录写入日志文件(通常是/var/log/audit/audit.log
)。auditd
也可以配置为实时发送警报或执行其他操作。
- 当决定记录一个事件(
性能考量:
- 审计,尤其是记录所有系统调用,会带来显著的性能开销。内核做了大量优化:
- BPF 过滤器: 规则编译成高效的字节码,在内核中快速评估。
- 快速路径/慢速路径: 如果
audit_context
为空且没有全局规则要求审计,内核会快速跳过审计检查。只有在需要时才创建audit_context
并执行完整规则匹配。 - 异步记录: 事件记录是异步进行的,放入队列后系统调用即可继续执行,不阻塞调用进程(
auditd
处理队列)。 - 选择性审计: 管理员应谨慎定义规则,只审计真正需要的特定系统调用、用户或文件,以最小化性能影响。
- 审计,尤其是记录所有系统调用,会带来显著的性能开销。内核做了大量优化:
总结流程:
- 用户进程发起系统调用(如
open
)。 - 内核陷入系统调用入口。
- 调用
audit_syscall_entry(arch, SYS_open, fd, pathname, flags, mode)
。 - 内核审计子系统用加载的 BPF 过滤器检查:
(arch, syscall=SYS_open, uid=..., pathname=...)
是否匹配任何规则? - 如果匹配:
- 为当前任务设置
audit_context
(如果还没有)。 - 在
audit_context
中标记需要审计此系统调用。 - 生成
SYSCALL
entry
记录(含syscall
,args
),放入队列。
- 为当前任务设置
- 内核执行实际的
open
系统调用代码。 - 系统调用执行完毕,准备返回用户空间。
- 调用
audit_syscall_exit(retval)
。 - 检查
audit_context
:如果之前entry
被标记需要审计:- 生成
SYSCALL
exit
记录(含syscall
,success
,exit=retval
),放入队列。
- 生成
- 内核返回系统调用结果 (
retval
) 给用户进程。 - 用户空间
auditd
从队列读取entry
和exit
记录,写入/var/log/audit/audit.log
。
关键点:
auditsyscall
不是单个函数,而是指代内核在系统调用入口/出口处触发审计记录的整个机制。- 审计发生在内核空间,由内核代码直接挂钩系统调用执行路径。
- 是否记录一个系统调用由用户配置的、在内核中运行的 BPF 规则决定。
- 记录包含极其详细的上下文信息(用户、进程、参数、结果)。
- 性能影响显著,必须精确配置规则以避免过度开销。
- 用户空间工具 (
auditctl
,auditd
,ausearch
,aureport
) 用于配置规则、收集和解读日志。
理解这个机制对于有效利用 Linux Audit 框架进行安全监控和合规性报告至关重要。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Helloeuler!