зеркало из https://github.com/mozilla/libaudit-go.git
165 строки
5.1 KiB
Go
165 строки
5.1 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package libaudit
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"syscall"
|
|
)
|
|
|
|
// EventCallback is the function definition for any function that wants to receive an AuditEvent as soon as
|
|
// it is received from the kernel. Error will be set to indicate any error that occurs while receiving
|
|
// messages.
|
|
type EventCallback func(*AuditEvent, error)
|
|
|
|
// RawEventCallback is similar to EventCallback but the difference is that the function will receive only
|
|
// the message string which contains the audit event and not the parsed AuditEvent struct.
|
|
type RawEventCallback func(string, error)
|
|
|
|
// AuditEvent is a parsed audit message.
|
|
type AuditEvent struct {
|
|
Serial string // Message serial
|
|
Timestamp string // Timestamp
|
|
Type string // Audit event type
|
|
Data map[string]string // Map of field values in the audit message
|
|
Raw string // Raw audit message from kernel
|
|
}
|
|
|
|
// NewAuditEvent takes a NetlinkMessage passed from the netlink connection and parses the data
|
|
// from the message header to return an AuditEvent type.
|
|
//
|
|
// Note that it is possible here that we don't have a full event to return. In some cases, a
|
|
// single audit event may be represented by multiple audit events from the kernel. This function
|
|
// looks after buffering partial fragments of a full event, and may only return the complete event
|
|
// once an AUDIT_EOE record has been recieved for the audit event.
|
|
//
|
|
// See https://www.redhat.com/archives/linux-audit/2016-January/msg00019.html for additional information
|
|
// on the behavior of this function.
|
|
func NewAuditEvent(msg NetlinkMessage) (*AuditEvent, error) {
|
|
x, err := ParseAuditEvent(string(msg.Data[:]), auditConstant(msg.Header.Type), true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if (*x).Type == "auditConstant("+strconv.Itoa(int(msg.Header.Type))+")" {
|
|
return nil, fmt.Errorf("unknown message type %d", msg.Header.Type)
|
|
}
|
|
|
|
// Determine if the event type is one which the kernel is expected to send only a single
|
|
// packet for; in these cases we don't need to look into buffering it and can return the
|
|
// event immediately.
|
|
if auditConstant(msg.Header.Type) < AUDIT_SYSCALL ||
|
|
auditConstant(msg.Header.Type) >= AUDIT_FIRST_ANOM_MSG {
|
|
return x, nil
|
|
}
|
|
|
|
// If this is an EOE message, get the entire processed message and return it.
|
|
if auditConstant(msg.Header.Type) == AUDIT_EOE {
|
|
return bufferGet(x), nil
|
|
}
|
|
|
|
// Otherwise we need to buffer this message.
|
|
bufferEvent(x)
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
// GetAuditEvents receives audit messages from the kernel and parses them into an AuditEvent.
|
|
// It passes them along the callback function and if any error occurs while receiving the message,
|
|
// the same will be passed in the callback as well.
|
|
//
|
|
// This function executes a go-routine (which does not return) and the function itself returns
|
|
// immediately.
|
|
func GetAuditEvents(s Netlink, cb EventCallback) {
|
|
go func() {
|
|
for {
|
|
select {
|
|
default:
|
|
msgs, _ := s.Receive(false)
|
|
for _, msg := range msgs {
|
|
if msg.Header.Type == syscall.NLMSG_ERROR {
|
|
err := int32(hostEndian.Uint32(msg.Data[0:4]))
|
|
if err != 0 {
|
|
cb(nil, fmt.Errorf("audit error %d", err))
|
|
}
|
|
} else {
|
|
nae, err := NewAuditEvent(msg)
|
|
if nae == nil {
|
|
continue
|
|
}
|
|
cb(nae, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// GetRawAuditEvents is similar to GetAuditEvents, however it returns raw messages and does not parse
|
|
// incoming audit data.
|
|
func GetRawAuditEvents(s Netlink, cb RawEventCallback) {
|
|
go func() {
|
|
for {
|
|
select {
|
|
default:
|
|
msgs, _ := s.Receive(false)
|
|
for _, msg := range msgs {
|
|
var (
|
|
m string
|
|
err error
|
|
)
|
|
if msg.Header.Type == syscall.NLMSG_ERROR {
|
|
v := int32(hostEndian.Uint32(msg.Data[0:4]))
|
|
if v != 0 {
|
|
cb(m, fmt.Errorf("audit error %d", v))
|
|
}
|
|
} else {
|
|
Type := auditConstant(msg.Header.Type)
|
|
if Type.String() == "auditConstant("+strconv.Itoa(int(msg.Header.Type))+")" {
|
|
err = errors.New("Unknown Type: " + string(msg.Header.Type))
|
|
} else {
|
|
m = "type=" + Type.String()[6:] +
|
|
" msg=" + string(msg.Data[:]) + "\n"
|
|
}
|
|
}
|
|
cb(m, err)
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// GetAuditMessages is a blocking function (runs in forever for loop) that receives audit messages
|
|
// from the kernel and parses them to AuditEvent. It passes them along the callback function and if
|
|
// any error occurs while receiving the message, the same will be passed in the callback as well.
|
|
//
|
|
// It will return when a signal is received on the done channel.
|
|
func GetAuditMessages(s Netlink, cb EventCallback, done *chan bool) {
|
|
for {
|
|
select {
|
|
case <-*done:
|
|
return
|
|
default:
|
|
msgs, _ := s.Receive(false)
|
|
for _, msg := range msgs {
|
|
if msg.Header.Type == syscall.NLMSG_ERROR {
|
|
v := int32(hostEndian.Uint32(msg.Data[0:4]))
|
|
if v != 0 {
|
|
cb(nil, fmt.Errorf("audit error %d", v))
|
|
}
|
|
} else {
|
|
nae, err := NewAuditEvent(msg)
|
|
if nae == nil {
|
|
continue
|
|
}
|
|
cb(nae, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|