Skip to content

Commit b373311

Browse files
authored
seclog: strip trailing whitespace from audit netlink message payload (#17384)
Should the payload be constructed using a logger that appends a newline (e.g. slog), the newline would be embedded verbatim in the netlink message and appear in journald output as a trailing newline inside the quoted message field (LP: #2160691). Strip all trailing whitespace in AuditWriter.Write before building the netlink message. The returned byte count still reflects the original input length to satisfy the io.Writer contract. Related: SNAPDENG-37246 Fixes: LP#2160691 Signed-off-by: Maciej Borzecki <maciej.borzecki@canonical.com>
1 parent 3ec52a2 commit b373311

2 files changed

Lines changed: 38 additions & 4 deletions

File tree

seclog/audit_linux.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package seclog
2121

2222
import (
23+
"bytes"
2324
"fmt"
2425
"sync/atomic"
2526
"syscall"
@@ -78,13 +79,20 @@ func OpenAuditWriter() (*AuditWriter, error) {
7879
return &AuditWriter{fd: fd, opened: true}, nil
7980
}
8081

81-
// Write sends payload as an AUDIT_TRUSTED_APP netlink message.
82-
// The returned byte count reflects only the original payload length.
83-
// Concurrent use requires external synchronization.
82+
// Write sends payload as an AUDIT_TRUSTED_APP netlink message. Each call must
83+
// carry a complete, well-formed message; partial writes are not supported.
84+
// Trailing whitespace is stripped before the payload is sent. The returned
85+
// byte count reflects the original payload length. Concurrent use requires
86+
// external synchronization.
8487
func (aw *AuditWriter) Write(payload []byte) (int, error) {
8588
if !aw.opened {
8689
return 0, fmt.Errorf("cannot send audit message: not open")
8790
}
91+
n := len(payload)
92+
// Strip trailing whitespace; should the payload be constructed by a logger
93+
// that appends a newline (e.g. slog), we remove it here since the audit
94+
// subsystem does not expect it.
95+
payload = bytes.TrimRight(payload, " \t\r\n")
8896
msg := aw.buildMessage(payload)
8997
addr := &syscall.SockaddrNetlink{
9098
Family: syscall.AF_NETLINK,
@@ -94,7 +102,7 @@ func (aw *AuditWriter) Write(payload []byte) (int, error) {
94102
if err := sys.Sendto(aw.fd, msg, 0, addr); err != nil {
95103
return 0, fmt.Errorf("cannot send audit message: %v", err)
96104
}
97-
return len(payload), nil
105+
return n, nil
98106
}
99107

100108
// Close closes the underlying netlink socket.

seclog/audit_linux_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,32 @@ func (s *AuditSuite) TestBuildMessageEmptyPayload(c *C) {
258258
c.Check(totalLen, Equals, uint32(20))
259259
}
260260

261+
func (s *AuditSuite) TestWriteStripsTrailingWhitespace(c *C) {
262+
for _, tc := range []struct {
263+
input string
264+
want string
265+
}{
266+
{"{\"foo\":\"bar\"}\n", "{\"foo\":\"bar\"}"},
267+
{"{\"foo\":\"bar\"} \t\r\n", "{\"foo\":\"bar\"}"},
268+
{"{\"foo\":\"bar\"}", "{\"foo\":\"bar\"}"},
269+
} {
270+
mock := &mockSyscallOps{socketFD: 7}
271+
restore := seclog.MockSyscallOps(mock)
272+
defer restore()
273+
274+
writer, err := seclog.OpenAuditWriter()
275+
c.Assert(err, IsNil)
276+
277+
n, err := writer.Write([]byte(tc.input))
278+
c.Assert(err, IsNil)
279+
c.Check(n, Equals, len(tc.input))
280+
281+
payload := mock.sendtoData[syscall.SizeofNlMsghdr:]
282+
c.Check(string(payload[:len(tc.want)]), Equals, tc.want)
283+
c.Check(payload[len(tc.want)], Equals, byte(0))
284+
}
285+
}
286+
261287
func (s *AuditSuite) TestNlmsgAlignAlreadyAligned(c *C) {
262288
c.Check(seclog.NlmsgAlign(0), Equals, uint32(0))
263289
c.Check(seclog.NlmsgAlign(4), Equals, uint32(4))

0 commit comments

Comments
 (0)