diff --git a/src/auditd-dispatch.c b/src/auditd-dispatch.c index 40bed7c8e..db7a1f5ed 100644 --- a/src/auditd-dispatch.c +++ b/src/auditd-dispatch.c @@ -75,24 +75,14 @@ int dispatch_event(const struct audit_reply *rep, int protocol_ver) e->hdr.type = rep->type; if (protocol_ver == AUDISP_PROTOCOL_VER) { - if (rep->nlh == &rep->msg.nlh) { - if (rep->msg.nlh.nlmsg_len < NLMSG_HDRLEN || - rep->msg.nlh.nlmsg_len > - NLMSG_LENGTH(sizeof(e->data))) { - free(e); - return -1; - } - - /* audit_get_reply marks embedded netlink replies this - * way. Netlink length includes its header, while - * event_t contains only payload. Local V0/legacy - * events retain a payload length here. */ - e->hdr.size = rep->msg.nlh.nlmsg_len - NLMSG_HDRLEN; - } else if (rep->msg.nlh.nlmsg_len > sizeof(e->data)) { + /* The kernel audit subsystem sets nlmsg_len to the payload + * size only (skb->len - NLMSG_HDRLEN), not the total + * message length. */ + if (rep->msg.nlh.nlmsg_len > sizeof(e->data)) { free(e); return -1; - } else - e->hdr.size = rep->msg.nlh.nlmsg_len; + } + 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; diff --git a/src/test/auditd_dispatch_test.c b/src/test/auditd_dispatch_test.c index dfdc169fe..265560a78 100644 --- a/src/test/auditd_dispatch_test.c +++ b/src/test/auditd_dispatch_test.c @@ -84,9 +84,10 @@ static void free_queued_event(void) } /* - * test_netlink_payload_length - verify netlink headers are not copied as data + * test_netlink_payload_length - kernel nlmsg_len is the payload size directly * - * Returns: None. + * The kernel audit subsystem sets nlmsg_len = skb->len - NLMSG_HDRLEN, + * i.e. the payload size only. Verify dispatch uses it as-is. */ static void test_netlink_payload_length(void) { @@ -96,7 +97,7 @@ static void test_netlink_payload_length(void) memset(rep.msg.data, 'a', sizeof(rep.msg.data)); rep.type = AUDIT_SYSCALL; rep.nlh = &rep.msg.nlh; - rep.msg.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(rep.msg.data)); + rep.msg.nlh.nlmsg_len = sizeof(rep.msg.data); assert(dispatch_event(&rep, AUDISP_PROTOCOL_VER) == 0); assert(queued_event != NULL); @@ -107,7 +108,7 @@ static void test_netlink_payload_length(void) } /* - * test_invalid_netlink_length - reject malformed embedded netlink lengths + * test_invalid_netlink_length - reject oversized payloads * * Returns: None. */ @@ -117,11 +118,7 @@ static void test_invalid_netlink_length(void) memset(&rep, 0, sizeof(rep)); rep.nlh = &rep.msg.nlh; - rep.msg.nlh.nlmsg_len = NLMSG_HDRLEN - 1; - assert(dispatch_event(&rep, AUDISP_PROTOCOL_VER) == -1); - assert(queued_event == NULL); - - rep.msg.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(rep.msg.data)) + 1; + rep.msg.nlh.nlmsg_len = sizeof(rep.msg.data) + 1; assert(dispatch_event(&rep, AUDISP_PROTOCOL_VER) == -1); assert(queued_event == NULL); } @@ -146,10 +143,67 @@ static void test_synthetic_payload_length(void) free_queued_event(); } +/* + * test_realistic_user_event - exercise a USER record like auditctl -m produces + * + * The kernel sets nlmsg_len to the payload size (not including the netlink + * header). The dispatcher must copy exactly that many bytes from msg.data. + */ +static void test_realistic_user_event(void) +{ + struct audit_reply rep; + const char *payload = + "audit(1721000000.123:42): pid=1234 uid=0 auid=0 ses=1 " + "subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 " + "msg='text=dispatch-test " + "exe=\"/usr/sbin/auditctl\" " + "hostname=? addr=? terminal=pts/0 res=success'"; + unsigned int plen = strlen(payload); + + memset(&rep, 0, sizeof(rep)); + rep.type = AUDIT_USER; + rep.nlh = &rep.msg.nlh; + rep.msg.nlh.nlmsg_len = plen; + memcpy(rep.msg.data, payload, plen); + + assert(dispatch_event(&rep, AUDISP_PROTOCOL_VER) == 0); + assert(queued_event != NULL); + assert(queued_event->hdr.size == plen); + assert(memcmp(queued_event->data, payload, plen) == 0); + free_queued_event(); +} + +/* + * test_v2_protocol_uses_rep_len - VER2 events use rep->len and rep->message + * + * Enriched / pre-formatted events travel as AUDISP_PROTOCOL_VER2 and must + * use the explicit rep->len field rather than nlmsg_len. + */ +static void test_v2_protocol_uses_rep_len(void) +{ + struct audit_reply rep; + const char *formatted = "type=USER msg=audit(1721000000.789:44): " + "op=user-msg terminal=pts/0 res=success"; + unsigned int flen = strlen(formatted); + + memset(&rep, 0, sizeof(rep)); + rep.type = AUDIT_USER; + rep.len = flen; + rep.message = (char *)formatted; + + assert(dispatch_event(&rep, AUDISP_PROTOCOL_VER2) == 0); + assert(queued_event != NULL); + assert(queued_event->hdr.size == flen); + assert(memcmp(queued_event->data, formatted, flen) == 0); + free_queued_event(); +} + int main(void) { test_netlink_payload_length(); test_invalid_netlink_length(); test_synthetic_payload_length(); + test_realistic_user_event(); + test_v2_protocol_uses_rep_len(); return 0; }