linux - 审核 netlink 响应没有正确的数据包长度

标签 linux go netlink audit-trail

我一直在尝试使用 mdlayher/netlink 从 go 读取 linux 审核日志。我能够建立连接并设置 PID,以便能够通过单播和多播从 netlink 套接字接收日志。

问题是,当库尝试解析来自 netlink 的消息时,它会失败,并且不是因为库的原因。我尝试转储发送到我的连接的消息,这就是我发现的。

([]uint8) (len=48 cap=4096) {
 00000000  1d 00 00 00 28 05 00 00  00 00 00 00 00 00 00 00  |....(...........|
 00000010  61 75 64 69 74 28 31 36  31 32 30 33 31 38 36 32  |audit(1612031862|
 00000020  2e 36 33 31 3a 32 37 31  30 34 29 3a 20 00 00 00  |.631:27104): ...|
}

这是来自日志流的消息之一。根据packet structure ,前 16 个字节是 netlink message header 的一部分nlmsghdr

struct nlmsghdr {
    __u32       nlmsg_len;  /* Length of message including header */
    __u16       nlmsg_type; /* Message content */
    __u16       nlmsg_flags;    /* Additional flags */
    __u32       nlmsg_seq;  /* Sequence number */
    __u32       nlmsg_pid;  /* Sending process port ID */
};

请注意,nlmsg_len 如何标记为包括 header 的消息长度。如果你查看消息转储,第一个 __u321d 00 00 00,它在网络字节顺序中是 29。这意味着整个数据包应该是 29 字节。但是,如果计算字节数,则会发现 45 + 3 个字节的填充,这就是数据包的对齐方式。消息在字节 45 处结束,即 (29 + 16) 或 29 + 消息头大小的长度。

但是,奇怪的是,它只发生在审核日志消息上,而不发生在审核控制消息回复上。引用https://play.golang.com/p/mA7_MJdVSv8有关如何解析数据包结构的示例。

这是预期的吗?查看 go stdlib syscall.ParseNetlinkMessage ,它似乎遵守 header + body 约束。我在 userspace-audit code 中看不出来它负责 auditdauditctl 及其工具系列。

另一个流行的库,slackhq/go-audit似乎不依赖 header 长度并根据从套接字读取的缓冲区大小进行解析。

mdlayher/netlink 库上的此差异似乎解决了上述问题,并且还可以获取有效负载的字节转储。但事实不应该如此。

diff --git a/conn_linux.go b/conn_linux.go
index ef18ef7..561ac69 100644
--- a/conn_linux.go
+++ b/conn_linux.go
@@ -11,6 +11,8 @@ import (
        "time"
        "unsafe"

+       "github.com/davecgh/go-spew/spew"
+       "github.com/josharian/native"
        "golang.org/x/net/bpf"
        "golang.org/x/sys/unix"
 )
@@ -194,7 +196,13 @@ func (c *conn) Receive() ([]Message, error) {

        raw, err := syscall.ParseNetlinkMessage(b[:n])
        if err != nil {
-               return nil, err
+               spew.Dump(b[:n])
+               bl := native.Endian.Uint32(b[:4]) + syscall.NLMSG_HDRLEN
+               native.Endian.PutUint32(b[:4], bl)
+               raw, err = syscall.ParseNetlinkMessage(b[:n])
+               if err != nil {
+                       return nil, err
+               }
        }

        msgs := make([]Message, 0, len(raw))

用于重现上述行为的代码


更新 1

当我尝试通过 AUDIT_SET 消息类型更改审核状态时,上述行为似乎突然出现。如果我尝试连接到只读多播组 AUDIT_NLGRP_READLOG,它似乎不会发生。另外,如果我关闭单播连接然后尝试多播,问题又会出现。基本上,只要我的 PID 绑定(bind)到套接字,这个问题就会再次出现。 仅通过多播组连接时的示例转储

([]uint8) (len=76 cap=4096) {
 00000000  49 00 00 00 1d 05 00 00  00 00 00 00 00 00 00 00  |I...............|
 00000010  61 75 64 69 74 28 31 36  31 32 31 36 35 31 33 31  |audit(1612165131|
 00000020  2e 30 31 36 3a 33 33 34  37 32 29 3a 20 61 72 67  |.016:33472): arg|
 00000030  63 3d 32 20 61 30 3d 22  61 75 64 69 74 63 74 6c  |c=2 a0="auditctl|
 00000040  22 20 61 31 3d 22 2d 73  22 00 00 00              |" a1="-s"...|
}

注意如果 0x49 = 73,则大小是确切的数据包长度。

最佳答案

所以我个人对此一无所知,但我通过搜索发现了这个问题,该搜索的另一个结果是这篇博客文章:https://blog.des.no/2020/08/netlink-auditing-and-counting-bytes/

总而言之,其他人也发现了这种行为,而且它似乎是一个错误。 以下是相关引用:

Bug #3: The length field on audit data messages does not include the length of the header.

This is jaw-dropping. It is so fundamentally wrong. It means that anyone who wants to talk to the audit subsystem using their own code instead of libaudit will have to add a workaround to the Netlink layer of their stack to either fix or ignore the error, and apply that workaround only for certain message types.

How has this gone unnoticed? Well, libaudit doesn’t do much input validation.

(...)

The odds of these bugs getting fixed is approximately zero, because existing applications will break in interesting ways if the kernel starts setting the length field correctly.

关于linux - 审核 netlink 响应没有正确的数据包长度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65972851/

相关文章:

c++ - 内存分配期间内存页断裂?

linux - 在文件名和文件内容都有约束的情况下,您将如何从 unix shell 进行搜索?

Golang 无法将 XML 映射到结构

c - 为什么我在使用 nl_recvmsgs 时收到 Netlink ERRORMSG?

linux - 在 NETLINK 消息中添加 MPLS 属性

linux - libevent 是否支持 netlink socket

linux - 将 plink 剂量 (.raw) 格式转换为 ped 格式

linux - 在专用服务器上安装Windows Server

go - 在方法或构造函数级别进行 Nil 处理?

pointers - 如何将接口(interface)实现指针转换为接口(interface)指针?