github.com/DataDog/datadog-agent/pkg/security/secl@v0.55.0-devel.0.20240517055856-10c4965fea94/model/process_cache_entry_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 holds model related files
     9  package model
    10  
    11  import (
    12  	"time"
    13  )
    14  
    15  // SetAncestor sets the ancestor
    16  func (pc *ProcessCacheEntry) SetAncestor(parent *ProcessCacheEntry) {
    17  	if pc.Ancestor == parent {
    18  		return
    19  	}
    20  
    21  	if pc.Ancestor != nil {
    22  		pc.Ancestor.Release()
    23  	}
    24  
    25  	pc.hasValidLineage = nil
    26  	pc.Ancestor = parent
    27  	pc.Parent = &parent.Process
    28  	parent.Retain()
    29  }
    30  
    31  func hasValidLineage(pc *ProcessCacheEntry) (bool, error) {
    32  	var (
    33  		pid, ppid uint32
    34  		ctrID     string
    35  		err       error
    36  	)
    37  
    38  	for pc != nil {
    39  		if pc.hasValidLineage != nil {
    40  			return *pc.hasValidLineage, pc.lineageError
    41  		}
    42  
    43  		pid, ppid, ctrID = pc.Pid, pc.PPid, pc.ContainerID
    44  
    45  		if pc.IsParentMissing {
    46  			err = &ErrProcessMissingParentNode{PID: pid, PPID: ppid, ContainerID: ctrID}
    47  		}
    48  
    49  		if pc.Pid == 1 {
    50  			if pc.Ancestor == nil {
    51  				return err == nil, err
    52  			}
    53  			return false, &ErrProcessWrongParentNode{PID: pid, PPID: pc.Ancestor.Pid, ContainerID: ctrID}
    54  		}
    55  		pc = pc.Ancestor
    56  	}
    57  
    58  	return false, &ErrProcessIncompleteLineage{PID: pid, PPID: ppid, ContainerID: ctrID}
    59  }
    60  
    61  // HasValidLineage returns false if, from the entry, we cannot ascend the ancestors list to PID 1 or if a new is having a missing parent
    62  func (pc *ProcessCacheEntry) HasValidLineage() (bool, error) {
    63  	res, err := hasValidLineage(pc)
    64  	pc.hasValidLineage, pc.lineageError = &res, err
    65  	return res, err
    66  }
    67  
    68  // Exit a process
    69  func (pc *ProcessCacheEntry) Exit(exitTime time.Time) {
    70  	pc.ExitTime = exitTime
    71  }
    72  
    73  func copyProcessContext(parent, child *ProcessCacheEntry) {
    74  	// inherit the container ID from the parent if necessary. If a container is already running when system-probe
    75  	// starts, the in-kernel process cache will have out of sync container ID values for the processes of that
    76  	// container (the snapshot doesn't update the in-kernel cache with the container IDs). This can also happen if
    77  	// the proc_cache LRU ejects an entry.
    78  	// WARNING: this is why the user space cache should not be used to detect container breakouts. Dedicated
    79  	// in-kernel probes will need to be added.
    80  	if len(parent.ContainerID) > 0 && len(child.ContainerID) == 0 {
    81  		child.ContainerID = parent.ContainerID
    82  	}
    83  }
    84  
    85  // ApplyExecTimeOf replace previous entry values by the given one
    86  func (pc *ProcessCacheEntry) ApplyExecTimeOf(entry *ProcessCacheEntry) {
    87  	pc.ExecTime = entry.ExecTime
    88  }
    89  
    90  // Exec replace a process
    91  func (pc *ProcessCacheEntry) Exec(entry *ProcessCacheEntry) {
    92  	entry.SetAncestor(pc)
    93  
    94  	// use exec time as exit time
    95  	pc.Exit(entry.ExecTime)
    96  	entry.Process.IsExecExec = !pc.IsThread
    97  
    98  	// keep some context
    99  	copyProcessContext(pc, entry)
   100  }
   101  
   102  // GetContainerPIDs return the pids
   103  func (pc *ProcessCacheEntry) GetContainerPIDs() []uint32 {
   104  	var pids []uint32
   105  
   106  	for pc != nil {
   107  		if pc.ContainerID == "" {
   108  			break
   109  		}
   110  		pids = append(pids, pc.Pid)
   111  
   112  		pc = pc.Ancestor
   113  	}
   114  
   115  	return pids
   116  }
   117  
   118  // SetParentOfForkChild set the parent of a fork child
   119  func (pc *ProcessCacheEntry) SetParentOfForkChild(parent *ProcessCacheEntry) {
   120  	pc.SetAncestor(parent)
   121  	if parent != nil {
   122  		pc.ArgsEntry = parent.ArgsEntry
   123  		pc.EnvsEntry = parent.EnvsEntry
   124  	}
   125  	pc.IsThread = true
   126  }
   127  
   128  // Fork returns a copy of the current ProcessCacheEntry
   129  func (pc *ProcessCacheEntry) Fork(childEntry *ProcessCacheEntry) {
   130  	childEntry.PPid = pc.Pid
   131  	childEntry.TTYName = pc.TTYName
   132  	childEntry.Comm = pc.Comm
   133  	childEntry.FileEvent = pc.FileEvent
   134  	childEntry.ContainerID = pc.ContainerID
   135  	childEntry.ExecTime = pc.ExecTime
   136  	childEntry.Credentials = pc.Credentials
   137  	childEntry.LinuxBinprm = pc.LinuxBinprm
   138  	childEntry.Cookie = pc.Cookie
   139  
   140  	childEntry.SetParentOfForkChild(pc)
   141  }
   142  
   143  // Equals returns whether process cache entries share the same values for file and args/envs
   144  func (pc *ProcessCacheEntry) Equals(entry *ProcessCacheEntry) bool {
   145  	return (pc.FileEvent.Equals(&entry.FileEvent) &&
   146  		pc.Credentials.Equals(&entry.Credentials) &&
   147  		pc.ArgsEntry.Equals(entry.ArgsEntry) &&
   148  		pc.EnvsEntry.Equals(entry.EnvsEntry))
   149  }
   150  
   151  func (pc *ProcessCacheEntry) markFileEventAsResolved() {
   152  	// mark file path as resolved
   153  	pc.FileEvent.SetPathnameStr("")
   154  	pc.FileEvent.SetBasenameStr("")
   155  
   156  	// mark interpreter as resolved too
   157  	pc.LinuxBinprm.FileEvent.SetPathnameStr("")
   158  	pc.LinuxBinprm.FileEvent.SetBasenameStr("")
   159  }
   160  
   161  // NewPlaceholderProcessCacheEntry returns a new empty process cache entry for failed process resolutions
   162  func NewPlaceholderProcessCacheEntry(pid uint32, tid uint32, isKworker bool) *ProcessCacheEntry {
   163  	entry := &ProcessCacheEntry{
   164  		ProcessContext: ProcessContext{
   165  			Process: Process{
   166  				PIDContext: PIDContext{Pid: pid, Tid: tid, IsKworker: isKworker},
   167  				Source:     ProcessCacheEntryFromPlaceholder,
   168  			},
   169  		},
   170  	}
   171  	entry.markFileEventAsResolved()
   172  	return entry
   173  }
   174  
   175  var processContextZero = ProcessCacheEntry{ProcessContext: ProcessContext{Process: Process{Source: ProcessCacheEntryFromPlaceholder}}}
   176  
   177  // GetPlaceholderProcessCacheEntry returns an empty process cache entry for failed process resolutions
   178  func GetPlaceholderProcessCacheEntry(pid uint32, tid uint32, isKworker bool) *ProcessCacheEntry {
   179  	processContextZero.Pid = pid
   180  	processContextZero.Tid = tid
   181  	processContextZero.IsKworker = isKworker
   182  	processContextZero.markFileEventAsResolved()
   183  	return &processContextZero
   184  }