auditbeat/module/file_integrity/kprobes/events_verifier.go (218 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 linux package kprobes import ( "errors" "os" "path/filepath" "sync" "time" "golang.org/x/sys/unix" ) type eventID struct { path string op uint32 } var eventGenerators = []func(*eventsVerifier, string, string) error{ // create file - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { file, err := os.OpenFile(targetFilePath, os.O_RDWR|os.O_CREATE, 0o644) if err != nil { return err } defer file.Close() e.addEventToExpect(targetFilePath, unix.IN_CREATE) return nil }, // truncate file - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := os.Truncate(targetFilePath, 0); err != nil { return err } e.addEventToExpect(targetFilePath, unix.IN_MODIFY) return nil }, // write to file - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { file, err := os.OpenFile(targetFilePath, os.O_WRONLY, 0o644) if err != nil { return err } defer file.Close() if _, err := file.WriteString("test"); err != nil { return err } e.addEventToExpect(targetFilePath, unix.IN_MODIFY) return nil }, // change owner of file - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := os.Chown(targetFilePath, os.Getuid(), os.Getgid()); err != nil { return err } e.addEventToExpect(targetFilePath, unix.IN_ATTRIB) return nil }, // change mode of file - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := os.Chmod(targetFilePath, 0o700); err != nil { return err } e.addEventToExpect(targetFilePath, unix.IN_ATTRIB) return nil }, // change times of file - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := unix.Utimes(targetFilePath, []unix.Timeval{ unix.NsecToTimeval(time.Now().UnixNano()), unix.NsecToTimeval(time.Now().UnixNano()), }); err != nil { return err } e.addEventToExpect(targetFilePath, unix.IN_ATTRIB) return nil }, // add attribute to file - generates 1 event // Note that this may fail if the filesystem doesn't support extended attributes // This is allVerified we just skip adding the respective event to verify func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { attrName := "user.myattr" attrValue := []byte("Hello, xattr!") if err := unix.Setxattr(targetFilePath, attrName, attrValue, 0); err != nil { if !errors.Is(err, unix.EOPNOTSUPP) { return err } } else { e.addEventToExpect(targetFilePath, unix.IN_ATTRIB) } return nil }, // move file - generates 2 events func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := os.Rename(targetFilePath, targetMovedFilePath); err != nil { return err } e.addEventToExpect(targetFilePath, unix.IN_MOVED_FROM) e.addEventToExpect(targetMovedFilePath, unix.IN_MOVED_TO) return nil }, // remove file - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := os.Remove(targetMovedFilePath); err != nil { return err } e.addEventToExpect(targetMovedFilePath, unix.IN_DELETE) return nil }, // create a directory - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := os.Mkdir(targetFilePath, 0o600); err != nil { return err } e.addEventToExpect(targetFilePath, unix.IN_CREATE) return nil }, // change mode of directory - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := os.Chmod(targetFilePath, 0o644); err != nil { return err } e.addEventToExpect(targetFilePath, unix.IN_ATTRIB) return nil }, // change owner of directory - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := os.Chown(targetFilePath, os.Getuid(), os.Getgid()); err != nil { return err } e.addEventToExpect(targetFilePath, unix.IN_ATTRIB) return nil }, // add attribute to directory - generates 1 event // Note that this may fail if the filesystem doesn't support extended attributes // This is allVerified we just skip adding the respective event to verify func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { attrName := "user.myattr" attrValue := []byte("Hello, xattr!") if err := unix.Setxattr(targetFilePath, attrName, attrValue, 0); err != nil { if !errors.Is(err, unix.EOPNOTSUPP) { return err } } else { e.addEventToExpect(targetFilePath, unix.IN_ATTRIB) } return nil }, // change times of directory - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := unix.Utimes(targetFilePath, []unix.Timeval{ unix.NsecToTimeval(time.Now().UnixNano()), unix.NsecToTimeval(time.Now().UnixNano()), }); err != nil { return err } e.addEventToExpect(targetFilePath, unix.IN_ATTRIB) return nil }, // move directory - generates 2 events func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := os.Rename(targetFilePath, targetMovedFilePath); err != nil { return err } e.addEventToExpect(targetFilePath, unix.IN_MOVED_FROM) e.addEventToExpect(targetMovedFilePath, unix.IN_MOVED_TO) return nil }, // remove the directory - generates 1 event func(e *eventsVerifier, targetFilePath string, targetMovedFilePath string) error { if err := os.Remove(targetMovedFilePath); err != nil { return err } e.addEventToExpect(targetMovedFilePath, unix.IN_DELETE) return nil }, } type eventsVerifier struct { sync.Mutex basePath string eventsToExpect map[eventID]int eventsToExpectNr int } func newEventsVerifier(basePath string) (*eventsVerifier, error) { return &eventsVerifier{ basePath: basePath, eventsToExpect: make(map[eventID]int), }, nil } func (e *eventsVerifier) validateEvent(path string, _ uint32, op uint32) error { e.Lock() defer e.Unlock() eID := eventID{ path: path, op: op, } _, exists := e.eventsToExpect[eID] if !exists { return ErrVerifyUnexpectedEvent } e.eventsToExpect[eID]-- return nil } // addEventToExpect adds an event to the eventsVerifier's list of expected events. func (e *eventsVerifier) addEventToExpect(path string, op uint32) { e.eventsToExpectNr++ eID := eventID{ path: path, op: op, } _, exists := e.eventsToExpect[eID] if !exists { e.eventsToExpect[eID] = 1 return } e.eventsToExpect[eID]++ } func (e *eventsVerifier) GenerateEvents() error { targetFilePath := filepath.Join(e.basePath, "validate_file") targetMovedFilePath := targetFilePath + "_moved" for _, genFunc := range eventGenerators { e.Lock() if err := genFunc(e, targetFilePath, targetMovedFilePath); err != nil { e.Unlock() return err } e.Unlock() } return nil } // Verified checks that all expected events filled during GenerateEvents() are present without any missing // or duplicated. func (e *eventsVerifier) Verified() error { if e.eventsToExpectNr == 0 { return ErrVerifyNoEventsToExpect } for _, status := range e.eventsToExpect { switch { case status < 0: return ErrVerifyOverlappingEvents case status > 0: return ErrVerifyMissingEvents } } return nil }