从源码分析,使用unix域流式套接字接收auditd分发程序audispd的二进制审计消息,篇三,分析audispd的消息协议并解析

370 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

源码分析通讯协议

从上一个章节可以看到,发出的消息由两部分组成,第一个部分是一个结构体,标识这个消息的一些基本信息,第二个部分是data数组

struct audit_dispatcher_header

  • 先看一下结构体的定义
    struct audit_dispatcher_header {
    	uint32_t	ver;	/* The version of this protocol */
    	uint32_t	hlen;	/* Header length */
    	uint32_t	type;	/* Message type */
    	uint32_t	size;	/* Size of data following the header */
    };
    
  • 可以看到,头的消息有四段,分别是
    • ver 协议版本号
    • hlen 结构体长度,固定为sizeof(struct audit_dispatcher_header)
    • type 消息类型
    • size 消息长度,这段在下面的data区很有用

ver 协议版本号

  • 有两种,测试的时候只遇到了ver2,这些消息是由auditd分发来的消息,具体看现在是否两种协议都使用需要再看auditd的源码了
    /usr/include/libaudit.h
    
    // Original protocol starts with msg='
    #define AUDISP_PROTOCOL_VER  0
    
    // Starts with node and/or type already in the text before msg=
    // IOW, its preformatted in the audit daemon.
    #define AUDISP_PROTOCOL_VER2 1
    
  • 两者的区别主要在计算data区域的长度问题上,但这个长度和二进制没有关系,只在使用字符串传递消息时计算的字符串长度,和我们使用的二进制传递消息没有关系
    /home/kira/rpmbuild/BUILD/audit-3.0.1/audisp/audispd.c
    
    // Protocol 1 is not formatted
    if (e->hdr.ver == AUDISP_PROTOCOL_VER) {
    	const char *type;
    
    	/* Get the event formatted */
    	type = audit_msg_type_to_name(e->hdr.type);
    	if (type == NULL) {
    		snprintf(unknown, sizeof(unknown),
    			"UNKNOWN[%u]", e->hdr.type);
    		type = unknown;
    	}
    	len = asprintf(&v, "type=%s msg=%.*s\n",
    			type, e->hdr.size, e->data);
    // Protocol 2 events are already formatted
    } else if (e->hdr.ver == AUDISP_PROTOCOL_VER2) {
    	len = asprintf(&v, "%.*s\n", e->hdr.size, e->data);
    } else
    	len = 0;
    

type 消息类型

  • 这里的type是由auditd传递给audispd的
    /home/kira/rpmbuild/BUILD/audit-3.0.1/src/auditd-dispatch.c
    
    int dispatch_event(const struct audit_reply *rep, int protocol_ver)
    {
    	event_t *e;
    
    	if (!libdisp_active())
    		return 0;
    
    	// Translate event into dispatcher format
    	e = malloc(sizeof(event_t));
    	if (e == NULL)
    		return -1;
    
    	e->hdr.ver = protocol_ver;
    	e->hdr.hlen = sizeof(struct audit_dispatcher_header);
    	e->hdr.type = rep->type;
    
  • type由宏定义出现在/usr/include/linux/audit.h
    只截取了一小部分
    ...  
    #define AUDIT_SYSCALL		1300	/* Syscall event */
    /* #define AUDIT_FS_WATCH	1301	 * Deprecated */
    #define AUDIT_PATH		1302	/* Filename path information */
    #define AUDIT_IPC		1303	/* IPC record */
    #define AUDIT_SOCKETCALL	1304	/* sys_socketcall arguments */
    #define AUDIT_CONFIG_CHANGE	1305	/* Audit system configuration change */
    #define AUDIT_SOCKADDR		1306	/* sockaddr copied as syscall arg */
    #define AUDIT_CWD		1307	/* Current working directory */
    #define AUDIT_EXECVE		1309	/* execve arguments */
    #define AUDIT_IPC_SET_PERM	1311	/* IPC new permissions record type */
    ...
    

data

  • 从这个函数可以看到,其实data的内容只是一段message,这里只是一段字符串消息
    /home/kira/rpmbuild/BUILD/audit-3.0.1/src/auditd-dispatch.c
    
    int dispatch_event(const struct audit_reply *rep, int protocol_ver)
    {
    	event_t *e;
    
    	if (!libdisp_active())
    		return 0;
    
    	// Translate event into dispatcher format
    	e = malloc(sizeof(event_t));
    	if (e == NULL)
    		return -1;
    
    	e->hdr.ver = protocol_ver;
    	e->hdr.hlen = sizeof(struct audit_dispatcher_header);
    	e->hdr.type = rep->type;
    
    	// Network originating events have data at rep->message
    	if (protocol_ver == AUDISP_PROTOCOL_VER) {
    		e->hdr.size = rep->msg.nlh.nlmsg_len;
    		memcpy(e->data, (void*)rep->msg.data, e->hdr.size);
    	} else if (protocol_ver == AUDISP_PROTOCOL_VER2) {
    		e->hdr.size = rep->len;
    		memcpy(e->data, (void*)rep->message, e->hdr.size);
    	} else {
    		free(e);
    		return 0;
    	}
    	return libdisp_enqueue(e);
    }
    
  • 同时这里也有一条比较关键的问题,可以看出data的空间是动态分配,并且没有初始化的,也没有在这段消息字符串后面跟上结束符,后面我们处理时,需要手动根据消息的长度加上分隔符

编写对应的接收消息

  • 这里是循环收消息的示例代码,创建unix sock的代码可以看看上文
    char buff[MAX_AUDIT_MESSAGE_LENGTH + sizeof(struct audit_dispatcher_header)]={0};
    
    while(1)
    {
        memset(buff, 0, sizeof(buff)); 
        int len;
    
        if((len = read(sockfd, buff, sizeof(buff))) > 0)
        {
            printf(">>>>>>>>>>>>>>> recv len = %d <<<<<<<<<<<<<<<\n", len);
            struct audit_dispatcher_header* audis = (struct audit_dispatcher_header*)buff;                   // 第一个字段
    
            printf("audis->ver = %u\n", audis->ver);            // 版本号,目前只有两个版本
            printf("audis->hlen = %u\n", audis->hlen);          // 头的长度,固定为 sizeof(struct audit_dispatcher_header)
            printf("audis->type = %u\n", audis->type);          // 消息类型
            printf("audis->size = %u\n", audis->size);          // 消息长度,注意打断收到的消息,源消息的内存空间是malloc出来的,不截断后续内容不保证
    
            switch (audis->ver)
            {
            case AUDISP_PROTOCOL_VER:
            case AUDISP_PROTOCOL_VER2:
                buff[sizeof(struct audit_dispatcher_header) + audis->size] = 0;
                break;
            
            default:
                printf("recv msg format erro\n");
                continue;
            }
    
            switch (audis->type)
            {
            case AUDIT_CWD:                 // 这些类型在audit.h中
                printf("work path reset");
                break;
            
            /**
             * 往后还有非常多的类型,这些类型都存储在audit.h中
             * 可以根据type解析对应的msg字符串
             */
    
            default:
    
                break;
            }
    
            printf("data >\n %s\n", (char*)((unsigned long)buff + sizeof(struct audit_dispatcher_header)));
        }
    }
    
  • 收到的消息示例,正常收到消息
    [kira@localhost source]$ ./test
    >>>>>>>>>>>>>>> recv len = 8986 <<<<<<<<<<<<<<<
    audis->ver = 1
    audis->hlen = 16
    audis->type = 2404
    audis->size = 405
    data >
     type=CRYPTO_KEY_USER msg=audit(1654740249.060:238): pid=2396 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=destroy kind=server fp=SHA256:bb:78:fd:e2:ed:6a:22:09:12:7d:6f:90:c2:dc:3a:0d:ca:d0:d2:58:95:79:3e:38:15:97:7d:1f:40:c3:11:49 direction=? spid=2396 suid=0  exe="/usr/sbin/sshd" hostname=? addr=? terminal=? res=success'UID="root" AUID="unset" SUID="root"
    >>>>>>>>>>>>>>> recv len = 8986 <<<<<<<<<<<<<<<
    audis->ver = 1
    audis->hlen = 16
    audis->type = 2407
    audis->size = 435
    data >
     type=CRYPTO_SESSION msg=audit(1654740249.062:239): pid=2395 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=start direction=from-server cipher=aes256-ctr ksize=256 mac=hmac-sha2-256 pfs=diffie-hellman-group-exchange-sha256 spid=2396 suid=74 rport=60919 laddr=192.168.58.131 lport=22  exe="/usr/sbin/sshd" hostname=? addr=192.168.58.1 terminal=? res=success'UID="root" AUID="unset" SUID="sshd"
    >>>>>>>>>>>>>>> recv len = 8986 <<<<<<<<<<<<<<<
    audis->ver = 1
    audis->hlen = 16
    audis->type = 2407
    audis->size = 435
    data >
     type=CRYPTO_SESSION msg=audit(1654740249.062:240): pid=2395 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 msg='op=start direction=from-client cipher=aes256-ctr ksize=256 mac=hmac-sha2-256 pfs=diffie-hellman-group-exchange-sha256 spid=2396 suid=74 rport=60919 laddr=192.168.58.131 lport=22  exe="/usr/sbin/sshd" hostname=? addr=192.168.58.1 terminal=? res=success'UID="root" AUID="unset" SUID="sshd"
    

完结、撒花