winlogbeat/sys/wineventlog/publisher_metadata.go (541 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//go:build windows
package wineventlog
import (
"fmt"
"os"
"syscall"
"go.uber.org/multierr"
"golang.org/x/sys/windows"
)
// PublisherMetadata provides methods to query metadata from an event log
// publisher.
type PublisherMetadata struct {
Name string // Name of the publisher/provider.
Handle EvtHandle // Handle to the publisher metadata from EvtOpenPublisherMetadata.
}
// Close releases the publisher metadata handle.
func (m *PublisherMetadata) Close() error {
return m.Handle.Close()
}
// NewPublisherMetadata opens the publisher's metadata. Close must be called on
// the returned PublisherMetadata to release its handle.
func NewPublisherMetadata(session EvtHandle, name string, locale uint32) (*PublisherMetadata, error) {
var publisherName, logFile *uint16
if info, err := os.Stat(name); err == nil && info.Mode().IsRegular() {
logFile, err = syscall.UTF16PtrFromString(name)
if err != nil {
return nil, err
}
} else {
publisherName, err = syscall.UTF16PtrFromString(name)
if err != nil {
return nil, err
}
}
handle, err := _EvtOpenPublisherMetadata(session, publisherName, logFile, 0, 0)
if err != nil {
return nil, fmt.Errorf("failed in EvtOpenPublisherMetadata: %w", err)
}
return &PublisherMetadata{
Name: name,
Handle: handle,
}, nil
}
func (m *PublisherMetadata) stringProperty(propertyID EvtPublisherMetadataPropertyID) (string, error) {
v, err := EvtGetPublisherMetadataProperty(m.Handle, propertyID)
if err != nil {
return "", err
}
switch t := v.(type) {
case string:
return t, nil
case nil:
return "", nil
default:
return "", fmt.Errorf("unexpected data type: %T", v)
}
}
func (m *PublisherMetadata) PublisherGUID() (windows.GUID, error) {
v, err := EvtGetPublisherMetadataProperty(m.Handle, EvtPublisherMetadataPublisherGuid)
if err != nil {
return windows.GUID{}, err
}
switch t := v.(type) {
case windows.GUID:
return t, nil
case nil:
return windows.GUID{}, nil
default:
return windows.GUID{}, fmt.Errorf("unexpected data type: %T", v)
}
}
func (m *PublisherMetadata) ResourceFilePath() (string, error) {
return m.stringProperty(EvtPublisherMetadataResourceFilePath)
}
func (m *PublisherMetadata) ParameterFilePath() (string, error) {
return m.stringProperty(EvtPublisherMetadataParameterFilePath)
}
func (m *PublisherMetadata) MessageFilePath() (string, error) {
return m.stringProperty(EvtPublisherMetadataMessageFilePath)
}
func (m *PublisherMetadata) HelpLink() (string, error) {
return m.stringProperty(EvtPublisherMetadataHelpLink)
}
func (m *PublisherMetadata) PublisherMessageID() (uint32, error) {
v, err := EvtGetPublisherMetadataProperty(m.Handle, EvtPublisherMetadataPublisherMessageID)
if err != nil {
return 0, err
}
return v.(uint32), nil
}
func (m *PublisherMetadata) PublisherMessage() (string, error) {
messageID, err := m.PublisherMessageID()
if err != nil {
return "", err
}
if int32(messageID) == -1 {
return "", nil
}
return getMessageStringFromMessageID(m, messageID, nil)
}
func (m *PublisherMetadata) Keywords() ([]MetadataKeyword, error) {
return NewMetadataKeywords(m.Handle)
}
func (m *PublisherMetadata) Opcodes() ([]MetadataOpcode, error) {
return NewMetadataOpcodes(m.Handle)
}
func (m *PublisherMetadata) Levels() ([]MetadataLevel, error) {
return NewMetadataLevels(m.Handle)
}
func (m *PublisherMetadata) Tasks() ([]MetadataTask, error) {
return NewMetadataTasks(m.Handle)
}
func (m *PublisherMetadata) Channels() ([]MetadataChannel, error) {
return NewMetadataChannels(m.Handle)
}
func (m *PublisherMetadata) EventMetadataIterator() (*EventMetadataIterator, error) {
return NewEventMetadataIterator(m)
}
type MetadataKeyword struct {
Name string
Mask uint64
Message string
MessageID uint32
}
func NewMetadataKeywords(publisherMetadataHandle EvtHandle) ([]MetadataKeyword, error) {
v, err := EvtGetPublisherMetadataProperty(publisherMetadataHandle, EvtPublisherMetadataKeywords)
if err != nil {
return nil, err
}
arrayHandle, ok := v.(EvtObjectArrayPropertyHandle)
if !ok {
return nil, fmt.Errorf("unexpected handle type: %T", v)
}
defer arrayHandle.Close()
arrayLen, err := EvtGetObjectArraySize(arrayHandle)
if err != nil {
return nil, fmt.Errorf("failed to get keyword array length: %w", err)
}
var values []MetadataKeyword
for i := uint32(0); i < arrayLen; i++ {
md, err := NewMetadataKeyword(publisherMetadataHandle, arrayHandle, i)
if err != nil {
return nil, fmt.Errorf("failed to get keyword at array index %v: %w", i, err)
}
values = append(values, *md)
}
return values, nil
}
func NewMetadataKeyword(publisherMetadataHandle EvtHandle, arrayHandle EvtObjectArrayPropertyHandle, index uint32) (*MetadataKeyword, error) {
v, err := EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataKeywordMessageID, index)
if err != nil {
return nil, err
}
messageID := v.(uint32)
// The value is -1 if the keyword did not specify a message attribute.
var message string
if int32(messageID) != -1 {
message, err = evtFormatMessage(publisherMetadataHandle, NilHandle, messageID, nil, EvtFormatMessageId)
if err != nil {
return nil, err
}
}
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataKeywordName, index)
if err != nil {
return nil, err
}
name := v.(string)
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataKeywordValue, index)
if err != nil {
return nil, err
}
valueMask := v.(uint64)
return &MetadataKeyword{
Name: name,
Mask: valueMask,
MessageID: messageID,
Message: message,
}, nil
}
type MetadataOpcode struct {
Name string
Opcode uint16
Task uint16
MessageID uint32
Message string
}
func NewMetadataOpcodes(publisherMetadataHandle EvtHandle) ([]MetadataOpcode, error) {
v, err := EvtGetPublisherMetadataProperty(publisherMetadataHandle, EvtPublisherMetadataOpcodes)
if err != nil {
return nil, err
}
arrayHandle, ok := v.(EvtObjectArrayPropertyHandle)
if !ok {
return nil, fmt.Errorf("unexpected handle type: %T", v)
}
defer arrayHandle.Close()
arrayLen, err := EvtGetObjectArraySize(arrayHandle)
if err != nil {
return nil, fmt.Errorf("failed to get opcode array length: %w", err)
}
var values []MetadataOpcode
for i := uint32(0); i < arrayLen; i++ {
md, err := NewMetadataOpcode(publisherMetadataHandle, arrayHandle, i)
if err != nil {
return nil, fmt.Errorf("failed to get opcode at array index %v: %w", i, err)
}
values = append(values, *md)
}
return values, nil
}
func NewMetadataOpcode(publisherMetadataHandle EvtHandle, arrayHandle EvtObjectArrayPropertyHandle, index uint32) (*MetadataOpcode, error) {
v, err := EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataOpcodeMessageID, index)
if err != nil {
return nil, err
}
messageID := v.(uint32)
// The value is -1 if the opcode did not specify a message attribute.
var message string
if int32(messageID) != -1 {
message, err = evtFormatMessage(publisherMetadataHandle, NilHandle, messageID, nil, EvtFormatMessageId)
if err != nil {
return nil, err
}
}
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataOpcodeName, index)
if err != nil {
return nil, err
}
name := v.(string)
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataOpcodeValue, index)
if err != nil {
return nil, err
}
// Mask high word contains the opcode value and the low word contains the task to which it belongs.
// If the low word is zero, the opcode is defined globally; otherwise, the opcode is task specific.
// Use the low word value to determine the task that defines the opcode.
valueMask := v.(uint32)
return &MetadataOpcode{
Name: name,
Opcode: uint16((valueMask >> 16) & 0xFFFF),
Task: uint16(valueMask & 0xFFFF),
MessageID: messageID,
Message: message,
}, nil
}
type MetadataLevel struct {
Name string
Mask uint32
MessageID uint32
Message string
}
func NewMetadataLevels(publisherMetadataHandle EvtHandle) ([]MetadataLevel, error) {
v, err := EvtGetPublisherMetadataProperty(publisherMetadataHandle, EvtPublisherMetadataLevels)
if err != nil {
return nil, err
}
arrayHandle, ok := v.(EvtObjectArrayPropertyHandle)
if !ok {
return nil, fmt.Errorf("unexpected handle type: %T", v)
}
defer arrayHandle.Close()
arrayLen, err := EvtGetObjectArraySize(arrayHandle)
if err != nil {
return nil, fmt.Errorf("failed to get level array length: %w", err)
}
var values []MetadataLevel
for i := uint32(0); i < arrayLen; i++ {
md, err := NewMetadataLevel(publisherMetadataHandle, arrayHandle, i)
if err != nil {
return nil, fmt.Errorf("failed to get level at array index %v: %w", i, err)
}
values = append(values, *md)
}
return values, nil
}
func NewMetadataLevel(publisherMetadataHandle EvtHandle, arrayHandle EvtObjectArrayPropertyHandle, index uint32) (*MetadataLevel, error) {
v, err := EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataLevelMessageID, index)
if err != nil {
return nil, err
}
messageID := v.(uint32)
// The value is -1 if the level did not specify a message attribute.
var message string
if int32(messageID) != -1 {
message, err = evtFormatMessage(publisherMetadataHandle, NilHandle, messageID, nil, EvtFormatMessageId)
if err != nil {
return nil, err
}
}
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataLevelName, index)
if err != nil {
return nil, err
}
name := v.(string)
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataLevelValue, index)
if err != nil {
return nil, err
}
valueMask := v.(uint32)
return &MetadataLevel{
Name: name,
Mask: valueMask,
MessageID: messageID,
Message: message,
}, nil
}
type MetadataTask struct {
Name string
Mask uint32
MessageID uint32
Message string
EventGUID windows.GUID
}
func NewMetadataTasks(publisherMetadataHandle EvtHandle) ([]MetadataTask, error) {
v, err := EvtGetPublisherMetadataProperty(publisherMetadataHandle, EvtPublisherMetadataTasks)
if err != nil {
return nil, err
}
arrayHandle, ok := v.(EvtObjectArrayPropertyHandle)
if !ok {
return nil, fmt.Errorf("unexpected handle type: %T", v)
}
defer arrayHandle.Close()
arrayLen, err := EvtGetObjectArraySize(arrayHandle)
if err != nil {
return nil, fmt.Errorf("failed to get task array length: %w", err)
}
var values []MetadataTask
for i := uint32(0); i < arrayLen; i++ {
md, err := NewMetadataTask(publisherMetadataHandle, arrayHandle, i)
if err != nil {
return nil, fmt.Errorf("failed to get task at array index %v: %w", i, err)
}
values = append(values, *md)
}
return values, nil
}
func NewMetadataTask(publisherMetadataHandle EvtHandle, arrayHandle EvtObjectArrayPropertyHandle, index uint32) (*MetadataTask, error) {
v, err := EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataTaskMessageID, index)
if err != nil {
return nil, err
}
messageID := v.(uint32)
// The value is -1 if the task did not specify a message attribute.
var message string
if int32(messageID) != -1 {
message, err = evtFormatMessage(publisherMetadataHandle, NilHandle, messageID, nil, EvtFormatMessageId)
if err != nil {
return nil, err
}
}
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataTaskName, index)
if err != nil {
return nil, err
}
name := v.(string)
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataTaskValue, index)
if err != nil {
return nil, err
}
valueMask := v.(uint32)
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataTaskEventGuid, index)
if err != nil {
return nil, err
}
guid := v.(windows.GUID)
return &MetadataTask{
Name: name,
Mask: valueMask,
MessageID: messageID,
Message: message,
EventGUID: guid,
}, nil
}
type MetadataChannel struct {
Name string
Index uint32
ID uint32
Message string
MessageID uint32
}
func NewMetadataChannels(publisherMetadataHandle EvtHandle) ([]MetadataChannel, error) {
v, err := EvtGetPublisherMetadataProperty(publisherMetadataHandle, EvtPublisherMetadataChannelReferences)
if err != nil {
return nil, err
}
arrayHandle, ok := v.(EvtObjectArrayPropertyHandle)
if !ok {
return nil, fmt.Errorf("unexpected handle type: %T", v)
}
defer arrayHandle.Close()
arrayLen, err := EvtGetObjectArraySize(arrayHandle)
if err != nil {
return nil, fmt.Errorf("failed to get task array length: %w", err)
}
var values []MetadataChannel
for i := uint32(0); i < arrayLen; i++ {
md, err := NewMetadataChannel(publisherMetadataHandle, arrayHandle, i)
if err != nil {
return nil, fmt.Errorf("failed to get task at array index %v: %w", i, err)
}
values = append(values, *md)
}
return values, nil
}
func NewMetadataChannel(publisherMetadataHandle EvtHandle, arrayHandle EvtObjectArrayPropertyHandle, index uint32) (*MetadataChannel, error) {
v, err := EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataChannelReferenceMessageID, index)
if err != nil {
return nil, err
}
messageID := v.(uint32)
// The value is -1 if the task did not specify a message attribute.
var message string
if int32(messageID) != -1 {
message, err = evtFormatMessage(publisherMetadataHandle, NilHandle, messageID, nil, EvtFormatMessageId)
if err != nil {
return nil, err
}
}
// Channel name.
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataChannelReferencePath, index)
if err != nil {
return nil, err
}
name := v.(string)
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataChannelReferenceIndex, index)
if err != nil {
return nil, err
}
channelIndex := v.(uint32)
v, err = EvtGetObjectArrayProperty(arrayHandle, EvtPublisherMetadataChannelReferenceID, index)
if err != nil {
return nil, err
}
id := v.(uint32)
return &MetadataChannel{
Name: name,
Index: channelIndex,
ID: id,
MessageID: messageID,
Message: message,
}, nil
}
type EventMetadataIterator struct {
Publisher *PublisherMetadata
eventMetadataEnumHandle EvtHandle
currentEvent EvtHandle
lastErr error
}
func NewEventMetadataIterator(publisher *PublisherMetadata) (*EventMetadataIterator, error) {
eventMetadataEnumHandle, err := _EvtOpenEventMetadataEnum(publisher.Handle, 0)
if err != nil && err != windows.ERROR_FILE_NOT_FOUND { //nolint:errorlint // Bad linter! This is always errno or nil.
return nil, fmt.Errorf("failed to open event metadata enumerator with EvtOpenEventMetadataEnum: %w (%#v)", err, err)
}
return &EventMetadataIterator{
Publisher: publisher,
eventMetadataEnumHandle: eventMetadataEnumHandle,
}, nil
}
func (itr *EventMetadataIterator) Close() error {
return multierr.Combine(
_EvtClose(itr.eventMetadataEnumHandle),
_EvtClose(itr.currentEvent),
)
}
// Next advances to the next event handle. It returns false when there are
// no more items or an error occurred. You should call Err() to check for an
// error.
func (itr *EventMetadataIterator) Next() bool {
if itr.eventMetadataEnumHandle == 0 {
// This is only the case when we could not find the event metadata file.
return false
}
// Close existing handle.
itr.currentEvent.Close()
var err error
itr.currentEvent, err = _EvtNextEventMetadata(itr.eventMetadataEnumHandle, 0)
if err != nil {
if err != windows.ERROR_NO_MORE_ITEMS { //nolint:errorlint // Bad linter! This is always errno or nil.
itr.lastErr = fmt.Errorf("failed advancing to next event metadata handle: %w", err)
}
return false
}
return true
}
// Err returns an error if Next() failed due to an error.
func (itr *EventMetadataIterator) Err() error {
return itr.lastErr
}
func typeCastError(expected, got interface{}) error {
return fmt.Errorf("wrong type for property. expected:%T got:%T", expected, got)
}
func (itr *EventMetadataIterator) uint32Property(propertyID EvtEventMetadataPropertyID) (uint32, error) {
v, err := GetEventMetadataProperty(itr.currentEvent, propertyID)
if err != nil {
return 0, err
}
value, ok := v.(uint32)
if !ok {
return value, typeCastError(value, v)
}
return value, nil
}
func (itr *EventMetadataIterator) uint64Property(propertyID EvtEventMetadataPropertyID) (uint64, error) {
v, err := GetEventMetadataProperty(itr.currentEvent, propertyID)
if err != nil {
return 0, err
}
value, ok := v.(uint64)
if !ok {
return value, typeCastError(value, v)
}
return value, nil
}
func (itr *EventMetadataIterator) stringProperty(propertyID EvtEventMetadataPropertyID) (string, error) {
v, err := GetEventMetadataProperty(itr.currentEvent, propertyID)
if err != nil {
return "", err
}
value, ok := v.(string)
if !ok {
return value, typeCastError(value, v)
}
return value, nil
}
func (itr *EventMetadataIterator) EventID() (uint32, error) {
return itr.uint32Property(EventMetadataEventID)
}
func (itr *EventMetadataIterator) Version() (uint32, error) {
return itr.uint32Property(EventMetadataEventVersion)
}
func (itr *EventMetadataIterator) Channel() (uint32, error) {
return itr.uint32Property(EventMetadataEventVersion)
}
func (itr *EventMetadataIterator) Level() (uint32, error) {
return itr.uint32Property(EventMetadataEventLevel)
}
func (itr *EventMetadataIterator) Opcode() (uint32, error) {
return itr.uint32Property(EventMetadataEventOpcode)
}
func (itr *EventMetadataIterator) Task() (uint32, error) {
return itr.uint32Property(EventMetadataEventTask)
}
func (itr *EventMetadataIterator) Keyword() (uint64, error) {
return itr.uint64Property(EventMetadataEventKeyword)
}
func (itr *EventMetadataIterator) MessageID() (uint32, error) {
return itr.uint32Property(EventMetadataEventMessageID)
}
func (itr *EventMetadataIterator) Template() (string, error) {
return itr.stringProperty(EventMetadataEventTemplate)
}
// Message returns the raw event description without doing any substitutions
// (e.g. the message will contain %1, %2, etc. as parameter placeholders).
func (itr *EventMetadataIterator) Message() (string, error) {
messageID, err := itr.MessageID()
if err != nil {
return "", err
}
// If the event definition does not specify a message, the value is –1.
if int32(messageID) == -1 {
return "", nil
}
return getMessageStringFromMessageID(itr.Publisher, messageID, nil)
}