Skip to content

Commit 7e4caa3

Browse files
committed
seclog: track audit fd open state to prevent accidental close of fd 0.
1 parent fe6a69d commit 7e4caa3

2 files changed

Lines changed: 49 additions & 3 deletions

File tree

seclog/audit_linux.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ func (realSyscallOps) Close(fd int) error {
6060
var sys syscallOps = realSyscallOps{}
6161

6262
// AuditWriter implements [io.WriteCloser].
63+
// It must be created via [OpenAuditWriter]; the zero value is not usable.
6364
type AuditWriter struct {
64-
fd int
65-
seq uint32
65+
fd int
66+
seq uint32
67+
opened bool
6668
}
6769

6870
// OpenAuditWriter opens a netlink audit socket and returns an [AuditWriter]
@@ -73,13 +75,16 @@ func OpenAuditWriter() (*AuditWriter, error) {
7375
if err != nil {
7476
return nil, fmt.Errorf("cannot open audit socket: %v", err)
7577
}
76-
return &AuditWriter{fd: fd}, nil
78+
return &AuditWriter{fd: fd, opened: true}, nil
7779
}
7880

7981
// Write sends payload as an AUDIT_TRUSTED_APP netlink message.
8082
// The returned byte count reflects only the original payload length.
8183
// Concurrent use requires external synchronization.
8284
func (aw *AuditWriter) Write(payload []byte) (int, error) {
85+
if !aw.opened {
86+
return 0, fmt.Errorf("cannot send audit message: not open")
87+
}
8388
msg := aw.buildMessage(payload)
8489
addr := &syscall.SockaddrNetlink{
8590
Family: syscall.AF_NETLINK,
@@ -94,6 +99,10 @@ func (aw *AuditWriter) Write(payload []byte) (int, error) {
9499

95100
// Close closes the underlying netlink socket.
96101
func (aw *AuditWriter) Close() error {
102+
if !aw.opened {
103+
return fmt.Errorf("cannot close audit writer: not open")
104+
}
105+
aw.opened = false
97106
return sys.Close(aw.fd)
98107
}
99108

seclog/audit_linux_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,43 @@ func (s *AuditSuite) TestClose(c *C) {
130130
c.Check(mock.closedFDs, DeepEquals, []int{7})
131131
}
132132

133+
func (s *AuditSuite) TestCloseNotOpen(c *C) {
134+
mock := &mockSyscallOps{
135+
socketFD: 7,
136+
}
137+
restore := seclog.MockSyscallOps(mock)
138+
defer restore()
139+
140+
writer, err := seclog.OpenAuditWriter()
141+
c.Assert(err, IsNil)
142+
143+
err = writer.Close()
144+
c.Assert(err, IsNil)
145+
146+
// Second close must fail.
147+
err = writer.Close()
148+
c.Assert(err, ErrorMatches, "cannot close audit writer: not open")
149+
// Only one fd was closed.
150+
c.Check(mock.closedFDs, DeepEquals, []int{7})
151+
}
152+
153+
func (s *AuditSuite) TestWriteAfterClose(c *C) {
154+
mock := &mockSyscallOps{
155+
socketFD: 7,
156+
}
157+
restore := seclog.MockSyscallOps(mock)
158+
defer restore()
159+
160+
writer, err := seclog.OpenAuditWriter()
161+
c.Assert(err, IsNil)
162+
163+
err = writer.Close()
164+
c.Assert(err, IsNil)
165+
166+
_, err = writer.Write([]byte("test"))
167+
c.Assert(err, ErrorMatches, "cannot send audit message: not open")
168+
}
169+
133170
func (s *AuditSuite) TestBuildMessageHeaderLayout(c *C) {
134171
aw := &seclog.AuditWriter{}
135172

0 commit comments

Comments
 (0)