github.com/DataDog/datadog-agent/pkg/security/secl@v0.55.0-devel.0.20240517055856-10c4965fea94/model/model_helpers_unix.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the Apache License Version 2.0.
     3  // This product includes software developed at Datadog (https://www.datadoghq.com/).
     4  // Copyright 2016-present Datadog, Inc.
     5  
     6  //go:build unix
     7  
     8  package model
     9  
    10  import (
    11  	"encoding/binary"
    12  	"errors"
    13  	"fmt"
    14  	"path"
    15  	"path/filepath"
    16  	"strings"
    17  	"syscall"
    18  	"time"
    19  
    20  	"github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval"
    21  )
    22  
    23  const (
    24  	OverlayFS = "overlay" // OverlayFS overlay filesystem
    25  	TmpFS     = "tmpfs"   // TmpFS tmpfs
    26  	UnknownFS = "unknown" // UnknownFS unknown filesystem
    27  
    28  	ErrPathMustBeAbsolute = "all the path have to be absolute"            // ErrPathMustBeAbsolute tells when a path is not absolute
    29  	ErrPathDepthLimit     = "path depths have to be shorter than"         // ErrPathDepthLimit tells when a path is too long
    30  	ErrPathSegmentLimit   = "each segment of a path must be shorter than" // ErrPathSegmentLimit tells when a patch reached the segment limit
    31  
    32  	// SizeOfCookie size of cookie
    33  	SizeOfCookie = 8
    34  )
    35  
    36  // check that all path are absolute
    37  func validatePath(field eval.Field, fieldValue eval.FieldValue) error {
    38  	// do not support regular expression on path, currently unable to support discarder for regex value
    39  	if fieldValue.Type == eval.RegexpValueType {
    40  		return fmt.Errorf("regexp not supported on path `%s`", field)
    41  	} else if fieldValue.Type == eval.VariableValueType {
    42  		return nil
    43  	}
    44  
    45  	if value, ok := fieldValue.Value.(string); ok {
    46  		errAbs := fmt.Errorf("invalid path `%s`, %s", value, ErrPathMustBeAbsolute)
    47  		errDepth := fmt.Errorf("invalid path `%s`, %s %d", value, ErrPathDepthLimit, MaxPathDepth)
    48  		errSegment := fmt.Errorf("invalid path `%s`, %s %d", value, ErrPathSegmentLimit, MaxSegmentLength)
    49  
    50  		if value == "" {
    51  			return nil
    52  		}
    53  
    54  		if value != path.Clean(value) {
    55  			return errAbs
    56  		}
    57  
    58  		if value == "*" {
    59  			return errAbs
    60  		}
    61  
    62  		if !filepath.IsAbs(value) && len(value) > 0 && value[0] != '*' {
    63  			return errAbs
    64  		}
    65  
    66  		if strings.HasPrefix(value, "~") {
    67  			return errAbs
    68  		}
    69  
    70  		// check resolution limitations
    71  		segments := strings.Split(value, "/")
    72  		if len(segments) > MaxPathDepth {
    73  			return errDepth
    74  		}
    75  		for _, segment := range segments {
    76  			if segment == ".." {
    77  				return errAbs
    78  			}
    79  			if len(segment) > MaxSegmentLength {
    80  				return errSegment
    81  			}
    82  		}
    83  	}
    84  
    85  	return nil
    86  }
    87  
    88  // ValidateField validates the value of a field
    89  func (m *Model) ValidateField(field eval.Field, fieldValue eval.FieldValue) error {
    90  	if strings.HasSuffix(field, "path") {
    91  		if err := validatePath(field, fieldValue); err != nil {
    92  			return err
    93  		}
    94  	}
    95  
    96  	switch field {
    97  
    98  	case "event.retval":
    99  		if value := fieldValue.Value; value != -int(syscall.EPERM) && value != -int(syscall.EACCES) {
   100  			return errors.New("return value can only be tested against EPERM or EACCES")
   101  		}
   102  	case "bpf.map.name", "bpf.prog.name":
   103  		if value, ok := fieldValue.Value.(string); ok {
   104  			if len(value) > MaxBpfObjName {
   105  				return fmt.Errorf("the name provided in %s must be at most %d characters, len(\"%s\") = %d", field, MaxBpfObjName, value, len(value))
   106  			}
   107  		}
   108  	}
   109  
   110  	if m.ExtraValidateFieldFnc != nil {
   111  		return m.ExtraValidateFieldFnc(field, fieldValue)
   112  	}
   113  
   114  	return nil
   115  }
   116  
   117  // SetPathResolutionError sets the Event.pathResolutionError
   118  func (ev *Event) SetPathResolutionError(fileFields *FileEvent, err error) {
   119  	fileFields.PathResolutionError = err
   120  	ev.Error = err
   121  }
   122  
   123  // Equals returns if both credentials are equal
   124  func (c *Credentials) Equals(o *Credentials) bool {
   125  	return c.UID == o.UID &&
   126  		c.GID == o.GID &&
   127  		c.EUID == o.EUID &&
   128  		c.EGID == o.EGID &&
   129  		c.FSUID == o.FSUID &&
   130  		c.FSGID == o.FSGID &&
   131  		c.CapEffective == o.CapEffective &&
   132  		c.CapPermitted == o.CapPermitted
   133  }
   134  
   135  // SetSpan sets the span
   136  func (p *Process) SetSpan(spanID uint64, traceID uint64) {
   137  	p.SpanID = spanID
   138  	p.TraceID = traceID
   139  }
   140  
   141  // GetPathResolutionError returns the path resolution error as a string if there is one
   142  func (p *Process) GetPathResolutionError() string {
   143  	return p.FileEvent.GetPathResolutionError()
   144  }
   145  
   146  // HasInterpreter returns whether the process uses an interpreter
   147  func (p *Process) HasInterpreter() bool {
   148  	return p.LinuxBinprm.FileEvent.Inode != 0
   149  }
   150  
   151  // IsNotKworker returns true if the process isn't a kworker
   152  func (p *Process) IsNotKworker() bool {
   153  	return !p.IsKworker
   154  }
   155  
   156  // GetProcessArgv returns the unscrubbed args of the event as an array. Use with caution.
   157  func (p *Process) GetProcessArgv() ([]string, bool) {
   158  	if p.ArgsEntry == nil {
   159  		return p.Argv, p.ArgsTruncated
   160  	}
   161  
   162  	argv := p.ArgsEntry.Values
   163  	if len(argv) > 0 {
   164  		argv = argv[1:]
   165  	}
   166  	p.Argv = argv
   167  	p.ArgsTruncated = p.ArgsTruncated || p.ArgsEntry.Truncated
   168  	return p.Argv, p.ArgsTruncated
   169  }
   170  
   171  // GetProcessArgv0 returns the first arg of the event and whether the process arguments are truncated
   172  func (p *Process) GetProcessArgv0() (string, bool) {
   173  	if p.ArgsEntry == nil {
   174  		return p.Argv0, p.ArgsTruncated
   175  	}
   176  
   177  	argv := p.ArgsEntry.Values
   178  	if len(argv) > 0 {
   179  		p.Argv0 = argv[0]
   180  	}
   181  	p.ArgsTruncated = p.ArgsTruncated || p.ArgsEntry.Truncated
   182  	return p.Argv0, p.ArgsTruncated
   183  }
   184  
   185  // Equals compares two FileFields
   186  func (f *FileFields) Equals(o *FileFields) bool {
   187  	return f.Inode == o.Inode && f.MountID == o.MountID && f.MTime == o.MTime && f.UID == o.UID && f.GID == o.GID && f.Mode == o.Mode
   188  }
   189  
   190  // IsFileless return whether it is a file less access
   191  func (f *FileFields) IsFileless() bool {
   192  	// TODO(safchain) fix this heuristic by add a flag in the event intead of using mount ID 0
   193  	return f.Inode != 0 && f.MountID == 0
   194  }
   195  
   196  // HasHardLinks returns whether the file has hardlink
   197  func (f *FileFields) HasHardLinks() bool {
   198  	return f.NLink > 1
   199  }
   200  
   201  // GetInLowerLayer returns whether a file is in a lower layer
   202  func (f *FileFields) GetInLowerLayer() bool {
   203  	return f.Flags&LowerLayer != 0
   204  }
   205  
   206  // GetInUpperLayer returns whether a file is in the upper layer
   207  func (f *FileFields) GetInUpperLayer() bool {
   208  	return f.Flags&UpperLayer != 0
   209  }
   210  
   211  // Equals compare two FileEvent
   212  func (e *FileEvent) Equals(o *FileEvent) bool {
   213  	return e.FileFields.Equals(&o.FileFields)
   214  }
   215  
   216  // SetPathnameStr set and mark as resolved
   217  func (e *FileEvent) SetPathnameStr(str string) {
   218  	e.PathnameStr = str
   219  	e.IsPathnameStrResolved = true
   220  }
   221  
   222  // SetBasenameStr set and mark as resolved
   223  func (e *FileEvent) SetBasenameStr(str string) {
   224  	e.BasenameStr = str
   225  	e.IsBasenameStrResolved = true
   226  }
   227  
   228  // GetPathResolutionError returns the path resolution error as a string if there is one
   229  func (e *FileEvent) GetPathResolutionError() string {
   230  	if e.PathResolutionError != nil {
   231  		return e.PathResolutionError.Error()
   232  	}
   233  	return ""
   234  }
   235  
   236  // IsOverlayFS returns whether it is an overlay fs
   237  func (e *FileEvent) IsOverlayFS() bool {
   238  	return e.Filesystem == "overlay"
   239  }
   240  
   241  // GetFSType returns the filesystem type of the mountpoint
   242  func (m *Mount) GetFSType() string {
   243  	return m.FSType
   244  }
   245  
   246  // IsOverlayFS returns whether it is an overlay fs
   247  func (m *Mount) IsOverlayFS() bool {
   248  	return m.GetFSType() == "overlay"
   249  }
   250  
   251  const (
   252  	ProcessCacheEntryFromUnknown     = iota // ProcessCacheEntryFromUnknown defines a process cache entry from unknown
   253  	ProcessCacheEntryFromPlaceholder        // ProcessCacheEntryFromPlaceholder defines the source of a placeholder process cache entry
   254  	ProcessCacheEntryFromEvent              // ProcessCacheEntryFromEvent defines a process cache entry from event
   255  	ProcessCacheEntryFromKernelMap          // ProcessCacheEntryFromKernelMap defines a process cache entry from kernel map
   256  	ProcessCacheEntryFromProcFS             // ProcessCacheEntryFromProcFS defines a process cache entry from procfs. Note that some exec parent may be missing.
   257  	ProcessCacheEntryFromSnapshot           // ProcessCacheEntryFromSnapshot defines a process cache entry from snapshot
   258  )
   259  
   260  // ProcessSources defines process sources
   261  var ProcessSources = [...]string{
   262  	"unknown",
   263  	"placeholder",
   264  	"event",
   265  	"map",
   266  	"procfs_fallback",
   267  	"procfs_snapshot",
   268  }
   269  
   270  // ProcessSourceToString returns the string corresponding to a process source
   271  func ProcessSourceToString(source uint64) string {
   272  	return ProcessSources[source]
   273  }
   274  
   275  // SetTimeout updates the timeout of an activity dump
   276  func (adlc *ActivityDumpLoadConfig) SetTimeout(duration time.Duration) {
   277  	adlc.Timeout = duration
   278  	adlc.EndTimestampRaw = adlc.StartTimestampRaw + uint64(duration)
   279  }
   280  
   281  // GetKey returns a key to uniquely identify a network device on the system
   282  func (d NetDevice) GetKey() string {
   283  	return fmt.Sprintf("%v_%v", d.IfIndex, d.NetNS)
   284  }
   285  
   286  func (p *PathKey) Write(buffer []byte) {
   287  	binary.NativeEndian.PutUint64(buffer[0:8], p.Inode)
   288  	binary.NativeEndian.PutUint32(buffer[8:12], p.MountID)
   289  	binary.NativeEndian.PutUint32(buffer[12:16], p.PathID)
   290  }
   291  
   292  // IsNull returns true if a key is invalid
   293  func (p *PathKey) IsNull() bool {
   294  	return p.Inode == 0 && p.MountID == 0
   295  }
   296  
   297  func (p *PathKey) String() string {
   298  	return fmt.Sprintf("%x/%x", p.MountID, p.Inode)
   299  }
   300  
   301  // MarshalBinary returns the binary representation of a path key
   302  func (p *PathKey) MarshalBinary() ([]byte, error) {
   303  	if p.IsNull() {
   304  		return nil, &ErrInvalidKeyPath{Inode: p.Inode, MountID: p.MountID}
   305  	}
   306  
   307  	buff := make([]byte, 16)
   308  	p.Write(buff)
   309  
   310  	return buff, nil
   311  }
   312  
   313  // PathKeySize defines the path key size
   314  const PathKeySize = 16
   315  
   316  // PathLeafSize defines path_leaf struct size
   317  const PathLeafSize = PathKeySize + MaxSegmentLength + 1 + 2 + 6 // path_key + name + len + padding
   318  
   319  // PathLeaf is the go representation of the eBPF path_leaf_t structure
   320  type PathLeaf struct {
   321  	Parent  PathKey
   322  	Name    [MaxSegmentLength + 1]byte
   323  	Len     uint16
   324  	Padding [6]uint8
   325  }
   326  
   327  // GetName returns the path value as a string
   328  func (pl *PathLeaf) GetName() string {
   329  	return NullTerminatedString(pl.Name[:])
   330  }
   331  
   332  // SetName sets the path name
   333  func (pl *PathLeaf) SetName(name string) {
   334  	copy(pl.Name[:], []byte(name))
   335  	pl.Len = uint16(len(name) + 1)
   336  }
   337  
   338  // MarshalBinary returns the binary representation of a path key
   339  func (pl *PathLeaf) MarshalBinary() ([]byte, error) {
   340  	buff := make([]byte, PathLeafSize)
   341  
   342  	pl.Parent.Write(buff)
   343  	copy(buff[16:], pl.Name[:])
   344  	binary.NativeEndian.PutUint16(buff[16+len(pl.Name):], pl.Len)
   345  
   346  	return buff, nil
   347  }
   348  
   349  // ResolveHashes resolves the hash of the provided file
   350  func (dfh *FakeFieldHandlers) ResolveHashes(_ EventType, _ *Process, _ *FileEvent) []string {
   351  	return nil
   352  }
   353  
   354  // ResolveUserSessionContext resolves and updates the provided user session context
   355  func (dfh *FakeFieldHandlers) ResolveUserSessionContext(_ *UserSessionContext) {}
   356  
   357  // SELinuxEventKind represents the event kind for SELinux events
   358  type SELinuxEventKind uint32
   359  
   360  const (
   361  	// SELinuxBoolChangeEventKind represents SELinux boolean change events
   362  	SELinuxBoolChangeEventKind SELinuxEventKind = iota
   363  	// SELinuxStatusChangeEventKind represents SELinux status change events
   364  	SELinuxStatusChangeEventKind
   365  	// SELinuxBoolCommitEventKind represents SELinux boolean commit events
   366  	SELinuxBoolCommitEventKind
   367  )
   368  
   369  // ExtraFieldHandlers handlers not hold by any field
   370  type ExtraFieldHandlers interface {
   371  	BaseExtraFieldHandlers
   372  	ResolveHashes(eventType EventType, process *Process, file *FileEvent) []string
   373  	ResolveUserSessionContext(evtCtx *UserSessionContext)
   374  }