【安全】2.6.6版本的audit审计机制分析

文章目录

    • 2.6.6版本的audit审计机制分析
      • 1 关于内核版本
      • 2 入口
      • 3 audit初始化
      • 4 系统调用审计流程
      • 5 配置下发流程
      • 6 总结

2.6.6版本的audit审计机制分析

1 关于内核版本

linux内核从2.6.6版本开始支持audit机制,为了更好的理解audit本身的机制,需要对audit的内核代码进行分析。

2 入口

内核中,audit相关的代码主要有三个文件:

  • include/linux/audit.h 头文件,主要包含常量、数据结构和接口声明
  • kernel/audit.c 全局变量定义以及接口的实现
  • kernel/auditsc.c 系统调用的审计的实现

从audit的功能来说,包含三个部分:

  • audit的初始化
  • 审计日志的生成和写入
  • 规则以及其他的操作函数

3 audit初始化

audit的初始化包括两个函数:

  • audit_init(),该函数用__initcall宏进行修饰,该函数会被放到.init.text段,在audit模块加载时会被调用,是整个audit模块的初始化函数
  • audit_enable(),该函数用__setup宏进行修饰,当bootloader传给kernel的参数是audit=0或者audit=1,就会调用audit_enable()函数,并将0或者1作为字符串参数传给audit_enable()

对于audit_init()函数,根据CONFIG_NET宏还定义了不同的实现,如果定义了CONFIG_NET,就会创建aduit的netlink的socket,否则,就不会创建socket。

4 系统调用审计流程

当前的audit版本只支持系统调用的审计,因此,主要查看系统调用的审计流程。

系统调用的入口位于arch/x86_64/kernel/entry.S,中间的x86_64随实际的架构有所变化。

ENTRY(system_call)CFI_STARTPROCswapgsmovq	%rsp,%gs:pda_oldrsp movq	%gs:pda_kernelstack,%rspsti					SAVE_ARGS 8,1movq  %rax,ORIG_RAX-ARGOFFSET(%rsp) movq  %rcx,RIP-ARGOFFSET(%rsp)  GET_THREAD_INFO(%rcx)testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),threadinfo_flags(%rcx)jnz tracesyscmpq $__NR_syscall_max,%raxja badsysmovq %r10,%rcxcall *sys_call_table(,%rax,8)  # XXX:	 rip relativemovq %rax,RAX-ARGOFFSET(%rsp)

上面是系统调用的入口,开始会处理一些参数,中间就会判断是否启用trace或者audit,如果启用,就会调用tracesys:

tracesys:			 SAVE_RESTmovq $-ENOSYS,RAX(%rsp)FIXUP_TOP_OF_STACK %rdimovq %rsp,%rdicall syscall_trace_enterLOAD_ARGS ARGOFFSET  /* reload args from stack in case ptrace changed it */RESTORE_RESTcmpq $__NR_syscall_max,%raxja  1fmovq %r10,%rcx	/* fixup for C */call *sys_call_table(,%rax,8)movq %rax,RAX-ARGOFFSET(%rsp)
1:	SAVE_RESTmovq %rsp,%rdicall syscall_trace_leaveRESTORE_TOP_OF_STACK %rbxRESTORE_RESTjmp ret_from_sys_call

这里就进行系统调用的审计,里面有三个call,中间的call就是调用系统调用,第一个call是系统调用入口的审计,第三个call是系统调用出口的审计。

// arch/x86_64/kernel/ptrace.c
asmlinkage void syscall_trace_enter(struct pt_regs *regs)
{if (unlikely(current->audit_context))audit_syscall_entry(current, regs->orig_rax,regs->rdi, regs->rsi,regs->rdx, regs->r10);if (test_thread_flag(TIF_SYSCALL_TRACE)&& (current->ptrace & PT_PTRACED))syscall_trace(regs);
}

这里的unlikely可以进行编译优化:Linux内核入门-- likely和unlikely。

后续调用流程如下:

  • audit_syscall_entry 位于kernel/auditsc.c
    • audit_alloc_context 创建audit_context
    • audit_filter_syscall
      • audit_filter_rules 对比进程的信息和规则确定是否匹配,此处实现的就是-F的过滤选项,过滤选项可以对产生日志的进程的字段进行过滤,该函数的返回值表明过滤选项是否匹配上,同时,会根据action设置state
    • 如果规则判断不是AUDIT_DISABLE,则将serial、time保存到context中,并在context中保存两个标记:in_syscall和auditable

系统调用结束时的调用流程:

  • audit_syscall_exit 位于kernel/auditsc.c
    • audit_get_context
      • 从task_struct中获取audit的context
      • audit_filter_syscall
      • 用进程的信息填充context,例如,填充pid:context->pid = tsk->pid
    • audit_log_exit 如果context中的in_syscall1和auditable1,则执行
      • audit_log_start
        • 如果audit_backlog大于audit_backlog_limit并且audit_rate_check()为真,则内核日志会出现audit: audit_backlog=XX > audit_backlog_limit=XX的打印
        • audit_log_lost audit_lost加1:atomic_inc(&audit_lost),如果rate_limit为0,则会在内核日志中打印audit: audit_lost=XX audit_backlog=XX audit_rate_limit=XX audit_backlog_limit=XX
        • 从audit_freelist链表中取一个audit_buffer,如果获取不到,则分配一个,如果分配失败,则调用audit_log_lost
        • audit_backlog加1:atomic_inc(&audit_backlog)
        • audit_log_format 将audit(second.milisecond:serial)写入刚才分配的audit_buffer
      • audit_log_format 先将syscall、per、exit、参数以及一些id作为审计日志输出,然后再输出系统调用过程中访问的文件和设备信息
        • audit_log_vformat 将要审计日志写入audit_buffer
      • audit_log_end
        • audit_log_end_irq 如果当前处于硬件中断上下文时调用,将audit_buffer放到audit_txlist队列
          • list_add_tail
          • tasklet_schedule(&audit_tasklet) 进行tasklet的任务调度,执行audit_tasklet
        • audit_log_end_fast 如果当前不处于硬件中断上下文时调用,直接将audit_buffer中的数据发送到用户空间
          • audit_log_move 将audit_buffer中的内容拷贝到sk_buff
          • audit_log_drain 遍历audit_buffer中的sk_buff,将sk_buffer发送到用户空间
            • 从audit_buffer中的sklist队列获取sk_buff,然后根据audit_pid将调用netlink_unicast将数据发送出去,如果多次发送失败,且错误码是EAGAIN,就会调用audit_log_end_irq将audit_buffer放到audit_txlist队列,其他错误则会打印错误日志;如果发送数据的返回值是ECONNREFUSED,则在内核日中打印audit: *NO* daemon at audit_pid=%d,并将audit_pid设置为0
            • 如果audit_pid为0,也就是没有进程订阅audit,则会将审计日志输出到内核日志
          • audit_backlog减1:atomic_dec(&audit_backlog)
          • kfree/list_add:如果当前空闲链表的节点过多,则直接将audit_buffer释放,否则将audit_buffer放到空闲链表
    • 将context中的in_syscall和auditable都设置为0
    • 释放context

从整个实现的流程看,主要依赖audit_context进行数据传递,在真正要执行系统调用前,创建audit_context,将参数保存到audit_context,并生成audit日志的audit(second.milisecond:serial)部分(保证单次系统调用生成的多条审计日志的这部分内容是一样的),将audit_context保存到进程的task_struct,在系统调用结束后,获取进程的audit_context,用进程的信息填充audit_context,再将数据写入到audit_buffer缓存中,最后要么直接通过netlink_unicast发送给用户态进程,要么将数据放到audit_txlist队列。

audit_log_exit()会将audit_context中的数据输出到审计日志:

struct audit_names {const char	*name;unsigned long	ino;dev_t		rdev;
};struct audit_context {int		    in_syscall;	/* 标记是否正在执行系统调用 */enum audit_state    state;unsigned int	    serial;     /* 审计日志的序列号 */struct timespec	    ctime;      /* 系统调用入口的时间 */uid_t		    loginuid;   /* login uid (identity) */int		    major;      /* 系统调用号 */unsigned long	    argv[4];    /* 系统调用参数 */int		    return_valid; /* 返回值是否有效 */int		    return_code;/* 系统调用返回值 */int		    auditable;  /* 标记是否需要写入审计日志 */int		    name_count;struct audit_names  names[AUDIT_NAMES];struct audit_context *previous; /* 系统调用嵌套 *//* 写入审计日志的字段 */pid_t		    pid;uid_t		    uid, euid, suid, fsuid;gid_t		    gid, egid, sgid, fsgid;unsigned long	    personality;#if AUDIT_DEBUGint		    put_count;int		    ino_count;
#endif
};

在打印日志时,会先输出一条日志,里面会输出pid到personality这些字段,以及上面的liginuid、major、argv、return_code等字段,然后就会打印names这个数组中的字段,这个里面是什么呢?看字面意思就是名字,是什么的名字呢?而且里面还有name、inode、rdev这些字段。其实,这里保存的就是系统调用执行过程中访问的文件或者目录的信息。

有两个地方会向names中加入元素:

  • fs/namei.c中的getname():将用户空间的文件名拷贝到内核空间,然后将文件名保存到audit_names
  • fs/namei.c中的path_lookup()调用audit_inode()将查找的路径的dentry的inode的i_ino和i_rdev保存到audit_names

names中的元素打印的格式类似于item=0 name=fname inode=11111 dev=xx:xx,因此,这里打印的就是系统调用过程中查找或者访问过的目录或者文件。

5 配置下发流程

上面是系统调用过程中,根据给定的规则然后输出审计日志,那么,审计规则是怎么来的呢?当用户在机器上执行auditctl -l或者auditctl -S进行审计规则的读取和设置时,依然是通过NETLINK_AUDIT的netlink socket进行通信的。audit自身的一些配置也是采用同样的流程。

当用户态程序需要增加规则时,通常会使用libaudit的audit_add_rule_data():

int audit_add_rule_data(int fd, struct audit_rule_data *rule,int flags, int action) {int rc;if (flags == AUDIT_FILTER_ENTRY) {audit_msg(LOG_WARNING, "Use of entry filter is deprecated");return -2;}rule->flags = flags;rule->action = action;rc = audit_send(fd, AUDIT_ADD_RULE, rule,sizeof (struct audit_rule_data) +rule->buflen);if (rc < 0)audit_msg(audit_priority(errno),"Error sending add rule data request (%s)",errno == EEXIST ?"Rule exists" : strerror(-rc));return rc;
}

这里的audit_send就是调用sendto系统调用向内核发送规则数据,调用sendto时,addr.nl_family设置为AF_NETLINK,下一步就会进入到内核的sys_sendto系统调用。

sys_sendto的调用流程如下:

  • sys_sendto
    • sock_sendmsg
      • __sock_sendmsg
        • sock->ops->sendmsg 调用套接字的sendmsg函数,此处不同的family类型就会调用不同的函数,对于AF_NETLINK来说就会调用netlink_sendmsg
          • netlink_unicast
            • netlink_attachskb
            • netlink_sendskb
              • sk->sk_data_ready 此处将sk_buff放到socket的接收队列的尾部,然后调用sk_data_ready进行处理

sk_data_ready是在创建内核netlink socket时设置的,通过netlink_data_ready最终调用到audit_init中的audit_receive:在audit_init初始化函数中,在创建netlink socket时会指定一个回调函数用于处理收到的数据:audit_sock = netlink_kernel_create(NETLINK_AUDIT, audit_receive)

audit_receive的调用链:

  • audit_receive:
    • skb_dequeue
      • audit_receive_skb
        • audit_receive_msg
        • netlink_ack

在audit_receive_msg中会根据收到的消息的类型执行不同的业务逻辑:

  • AUDIT_GET:用户态程序查询audit的状态,对应auditctl -s命令

  • AUDIT_SET:用户态程序设置audit的配置,对应auditctl的设置命令,例如auditctl -b设置backlog_limit,auditctl -e设置enabled,auditctl --reset-lost重置lost

  • AUDIT_USER:接收用户态发送的数据,直接写入到审计日志,对应auditctl -m命令

  • AUDIT_LOGIN:用户和注销时的审计日志

  • AUDIT_LIST:列出系统调用监控规则,对应auditctl -l命令

  • AUDIT_ADD:增加系统调用监控规则,对应auditctl -a命令

  • AUDIT_DEL:删除系统调用监控规则,对应auditctl -d命令

  • audit_receive_msg:

    • audit_receive_filter:
      • AUDIT_LIST:向用户态程序发送audit_tsklist、audit_entlist、audit_extlist的数据,然后发送一个空
      • AUDIT_ADD:将用户空间的规则拷贝到audit_entry,然后根据规则类型,加入到不同的链表
      • AUDIT_DEL:根据规则类型,删除不同的链表中的元素

所以,规则的操作也就是操作audit_tsklist、audit_entlist、audit_extlist三个链表,这三个链表的含义分别是:task、entry、exit。

audit_tsklist的调用链:

  • sys_fork arch/x86_64/kernel/process.c
    • do_fork kernel/fork.c
      • copy_process
        • audit_alloc kernel/auditsc.c
          • audit_filter_task
            • audit_filter_rules(audit_tsklist)

audit_entlist的调用链:

  • audit_syscall_entry
    • audit_filter_syscall(audit_entlist)

audit_extlist的调用链:

  • audit_syscall_exit
    • audit_get_context
      • audit_filter_syscall(audit_extlist)

6 总结

从整个流程来说需要了解以下内容:

  • 用户态程序和内核态程序通过netlink机制进行交互,且接口与网络套接字的一致,都是采用类似sendto/recvfrom的系统调用,只是里面的family字段不同
  • 2.6.6版本的audit只支持系统调用的审计,不支持文件和目录的监控
  • 2.6.6版本的audit中没有内核态的kaudit线程,审计日志的发送在系统调用退出阶段,相当于是个同步的发送过程,可以想象,当审计日志数量比较大时,可能会影响系统调用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/171140.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器人开发的选择

喷涂机器人 码垛机器人 纸箱码垛机器人 焊接机器人 跳舞机器人 管道清理机器人 工地巡检机器人 点餐机器人 化工巡检机器人 装箱机器人 安防巡检机器人 迎宾机器人好像有点像软银那个 污水管道检测机器人 大酒店用扫地机器人 家用扫地机器人 工厂用&#xff08;…

知识图谱06——将pdf中的表格(文字形式)保存至csv中

使用ubuntu22.04&#xff0c;anaconda 由于装环境装了一阵子&#xff0c;不确定装了哪些包了 可能的环境安装 conda install -c conda-forge pymupdf conda install -c conda-forge camelot-py conda install pandas #或者 pip install PyMuPDF pip install camelot-py[all] …

求链表环的起始位置

leetcode中题目位置 https://leetcode.cn/problems/linked-list-cycle-ii/submissions/?envTypestudy-plan-v2&envIdtop-100-liked 代码&#xff1a; public class Solution {public ListNode detectCycle(ListNode head) {if (head null || head.next null) {return…

S25FL系列FLASH读写的FPGA实现

文章目录 实现思路具体实现子模块实现top模块 测试Something 实现思路 建议读者先对 S25FL-S 系列 FLASH 进行了解&#xff0c;我之前的博文中有详细介绍。 笔者的芯片具体型号为 S25FL256SAGNFI00&#xff0c;存储容量 256Mb&#xff0c;增强高性能 EHPLC&#xff0c;4KB 与 6…

Could not resolve all files for configuration ‘:app:androidJdkImage‘.

在使用./gradlew build编译项目时候遇到了该问题&#xff0c;整体错误如下: * What went wrong: Configuration cache state could not be cached: field generatedModuleFile of com.android.build.gradle.tasks.JdkImageInput bean found in field compilerArgumentProvider…

Leetcode199. 二叉树的右视图

Every day a Leetcode 题目来源&#xff1a;199. 二叉树的右视图 解法1&#xff1a;层序遍历 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 按层序遍历&#xff0c;将每层的…

如何判断一个题目用“贪心/动态规划“还是用“BFS/DFS”方法解决

1 总结 1.1 贪心、动态规划和BFS/DFS题解的关系 一般能使用贪心、动态规划解决一个问题时&#xff0c;使用BFS&#xff0c;DFS也能解决这个题&#xff0c;但是反之不能成立。 1.2 2 贪心 -> BFS/DFS 2.1 跳跃游戏1和3的异同 这两道题&#xff0c;“跳跃游戏”&#xf…

html实现各种瀑布流(附源码)

文章目录 1.设计来源1.1 动态响应瀑布流1.2 分页瀑布流1.3 响应瀑布流 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/134613121 html实现各种瀑布流(附源码)&#xff0c;…

100元预算,轻松涨粉1000!腾讯运营面试秘籍大揭秘!

大家好啊&#xff01;小米在这里&#xff5e; 很高兴又有机会和大家见面啦&#xff01;最近小米参加了一场腾讯的运营面试&#xff0c;遇到了一个超有趣的问题&#xff1a;如果让你运营一个公众号&#xff0c;近期需要增加1000个关注&#xff0c;预算100元&#xff0c;怎么完成…

CMake中的变量: 描述系统信息的变量

文章目录 变量名称描述ANDROID当目标系统&#xff08;CMAKE_SYSTEM_NAME&#xff09;为Android时&#xff0c;设置为1。APPLE当目标系统是苹果平台&#xff08;macOS、iOS、tvOS、visionOS或watchOS&#xff09;时&#xff0c;设置为True。BORLAND如果正在使用Borland编译器&am…

【阿里云】图像识别 智能分类识别 项目开发(一)

语音模块和阿里云图像识别结合 环境准备 代码实现 编译运行 写个shell脚本用于杀死运行的进程 语音模块和阿里云图像识别结合 使用语音模块和摄像头在香橙派上做垃圾智能分类识别 语音控制摄像下载上传阿里云解析功能点实现 环境准备 将语音模块接在UART5的位置 在orange…

数据结构总复习

文章目录 线性表动态分配的顺序存储结构链式存储 线性表 动态分配的顺序存储结构 通过分析代码&#xff0c;我们发现&#xff0c;要注意什么&#xff1a; 要分清你的下标Insert 函数是可以用来没有元素的时候&#xff0c;增加元素的Init(或者Create )函数一般只用来分配空间…

qt 简单了解QHBoxLayout QVBoxLayout QFormLayout水平,垂直,表单布局管理器.

QHBoxLayout水平布局,QVBoxLayout垂直布局,QFormLayout表单布局管理器,是常用的布局管理器,是用代码编写应用界面必不可少的功能类. 1.tips 这里值得注意的是,2个单选按钮(QRadioButton)同时放进一个水平布局管理器(QHBoxLayout)中,相当于放进了一个分组器中,此时,2个单选按钮…

C++判断一个整数是否为回文数

可以将整数转换为字符串&#xff0c;然后再判断该字符串是否为回文串。 将整数转化为字符串&#xff0c;可以使用 to_string() 方法&#xff1b; 使用双指针法判断字符串是否为回文串。 #include <iostream> #include <string>using namespace std;bool isPalin…

Qt QString与QChar总结

(一) QString 1 QString的简介 QString 是Qt 中的一个类&#xff0c;用于存储字符串&#xff0c;QString 没有父类。QString 存储的是一串字符&#xff0c;每个字符是一个 QChar 类型的数据。QChar 使用的是 UTF-16 编码&#xff0c;一个字符包含 2字节数据。 对于超过 6553…

5 个 Python 异常值检测算法实战案例

异常值检测(outlier)是一种数据挖掘过程,用于确定数据集中发现的异常值并确定其出现的详细信息。当前自动异常检测至关重要,因为大量数据无法手动标记异常值。自动异常检测具有广泛的应用,例如信用卡欺诈检测,系统健康监测,故障检测以及传感器网络中的事件检测系统等。 …

go atexit源码分析

文章目录 atexit源码解析UML类图样例一: 程序退出之前执行注册函数1.1 流程图1.2 代码分析 样例二&#xff1a;使用cancel取消注册函数2.1 cancel流程图2.2 代码分析 样例三&#xff1a;使用Fatal/Fatalln/Fatal执行注册函数3.1 Fatal/Fatalln/Fatal流程图3.2 代码分析 atexit源…

Android平台GB28181设备接入模块开发填坑指南

技术背景 为什么要开发Android平台GB28181设备接入模块&#xff1f;这个问题不再赘述&#xff0c;在做Android平台GB28181客户端的时候&#xff0c;媒体数据这块&#xff0c;我们已经有了很好的积累&#xff0c;因为在此之前&#xff0c;我们就开发了非常成熟的RTMP推送、轻量…

Scannet v2 数据集介绍以及子集下载展示

Scannet v2 数据集介绍以及子集下载展示 文章目录 Scannet v2 数据集介绍以及子集下载展示参考数据集简介子集scannet_frames_25kscannet_frames_test 下载脚本 download_scannetv2.py 参考 scannet数据集简介和下载-CSDN博客 scannet v2 数据集下载_scannetv2数据集_蓝羽飞鸟的…

kafka学习笔记(一)--脑裂

我知道你想裂&#xff0c;但你先别裂 脑裂 用集群部署的大多数的分布式系统无可避免会面临脑裂问题。简单来说&#xff0c;脑裂就是在同一时刻出现了两个“Leader&#xff08;或叫Master&#xff09;”。设想这样一个场景&#xff1a;某分布式系统的分别部署在A&#xff0c;B…