github.com/DataDog/datadog-agent/pkg/security/secl@v0.55.0-devel.0.20240517055856-10c4965fea94/model/model.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:generate stringer -type=HashState -linecomment -output model_string.go
     7  
     8  // Package model holds model related files
     9  package model
    10  
    11  import (
    12  	"net"
    13  	"reflect"
    14  	"runtime"
    15  	"time"
    16  	"unsafe"
    17  
    18  	"github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval"
    19  	"github.com/DataDog/datadog-agent/pkg/security/secl/model/usersession"
    20  )
    21  
    22  // Model describes the data model for the runtime security agent events
    23  type Model struct {
    24  	ExtraValidateFieldFnc func(field eval.Field, fieldValue eval.FieldValue) error
    25  }
    26  
    27  var eventZero = Event{BaseEvent: BaseEvent{ContainerContext: &ContainerContext{}, Os: runtime.GOOS}}
    28  var containerContextZero ContainerContext
    29  
    30  // NewEvent returns a new Event
    31  func (m *Model) NewEvent() eval.Event {
    32  	return &Event{
    33  		BaseEvent: BaseEvent{
    34  			ContainerContext: &ContainerContext{},
    35  			Os:               runtime.GOOS,
    36  		},
    37  	}
    38  }
    39  
    40  // NewDefaultEventWithType returns a new Event for the given type
    41  func (m *Model) NewDefaultEventWithType(kind EventType) eval.Event {
    42  	return &Event{
    43  		BaseEvent: BaseEvent{
    44  			Type:             uint32(kind),
    45  			FieldHandlers:    &FakeFieldHandlers{},
    46  			ContainerContext: &ContainerContext{},
    47  		},
    48  	}
    49  }
    50  
    51  // Releasable represents an object than can be released
    52  type Releasable struct {
    53  	onReleaseCallback func() `field:"-"`
    54  }
    55  
    56  // CallReleaseCallback calls the on-release callback
    57  func (r *Releasable) CallReleaseCallback() {
    58  	if r.onReleaseCallback != nil {
    59  		r.onReleaseCallback()
    60  	}
    61  }
    62  
    63  // SetReleaseCallback sets a callback to be called when the cache entry is released
    64  func (r *Releasable) SetReleaseCallback(callback func()) {
    65  	previousCallback := r.onReleaseCallback
    66  	r.onReleaseCallback = func() {
    67  		callback()
    68  		if previousCallback != nil {
    69  			previousCallback()
    70  		}
    71  	}
    72  }
    73  
    74  // OnRelease triggers the callback
    75  func (r *Releasable) OnRelease() {
    76  	r.onReleaseCallback()
    77  }
    78  
    79  // ContainerContext holds the container context of an event
    80  type ContainerContext struct {
    81  	Releasable
    82  	ID        string   `field:"id,handler:ResolveContainerID"`                              // SECLDoc[id] Definition:`ID of the container`
    83  	CreatedAt uint64   `field:"created_at,handler:ResolveContainerCreatedAt"`               // SECLDoc[created_at] Definition:`Timestamp of the creation of the container``
    84  	Tags      []string `field:"tags,handler:ResolveContainerTags,opts:skip_ad,weight:9999"` // SECLDoc[tags] Definition:`Tags of the container`
    85  	Resolved  bool     `field:"-"`
    86  }
    87  
    88  // SecurityProfileContext holds the security context of the profile
    89  type SecurityProfileContext struct {
    90  	Name       string      `field:"name"`        // SECLDoc[name] Definition:`Name of the security profile`
    91  	Version    string      `field:"version"`     // SECLDoc[version] Definition:`Version of the security profile`
    92  	Tags       []string    `field:"tags"`        // SECLDoc[tags] Definition:`Tags of the security profile`
    93  	EventTypes []EventType `field:"event_types"` // SECLDoc[event_types] Definition:`Event types enabled for the security profile`
    94  }
    95  
    96  // IPPortContext is used to hold an IP and Port
    97  type IPPortContext struct {
    98  	IPNet net.IPNet `field:"ip"`   // SECLDoc[ip] Definition:`IP address`
    99  	Port  uint16    `field:"port"` // SECLDoc[port] Definition:`Port number`
   100  }
   101  
   102  // NetworkContext represents the network context of the event
   103  type NetworkContext struct {
   104  	Device NetworkDeviceContext `field:"device"` // network device on which the network packet was captured
   105  
   106  	L3Protocol  uint16        `field:"l3_protocol"` // SECLDoc[l3_protocol] Definition:`l3 protocol of the network packet` Constants:`L3 protocols`
   107  	L4Protocol  uint16        `field:"l4_protocol"` // SECLDoc[l4_protocol] Definition:`l4 protocol of the network packet` Constants:`L4 protocols`
   108  	Source      IPPortContext `field:"source"`      // source of the network packet
   109  	Destination IPPortContext `field:"destination"` // destination of the network packet
   110  	Size        uint32        `field:"size"`        // SECLDoc[size] Definition:`size in bytes of the network packet`
   111  }
   112  
   113  // SpanContext describes a span context
   114  type SpanContext struct {
   115  	SpanID  uint64 `field:"_"`
   116  	TraceID uint64 `field:"_"`
   117  }
   118  
   119  // BaseEvent represents an event sent from the kernel
   120  type BaseEvent struct {
   121  	ID            string         `field:"-" event:"*"`
   122  	Type          uint32         `field:"-"`
   123  	Flags         uint32         `field:"-"`
   124  	TimestampRaw  uint64         `field:"event.timestamp,handler:ResolveEventTimestamp" event:"*"` // SECLDoc[event.timestamp] Definition:`Timestamp of the event`
   125  	Timestamp     time.Time      `field:"timestamp,opts:getters_only,handler:ResolveEventTime" event:"*"`
   126  	Rules         []*MatchedRule `field:"-"`
   127  	ActionReports []ActionReport `field:"-"`
   128  	Os            string         `field:"event.os" event:"*"`                             // SECLDoc[event.os] Definition:`Operating system of the event`
   129  	Origin        string         `field:"event.origin" event:"*"`                         // SECLDoc[event.origin] Definition:`Origin of the event`
   130  	Service       string         `field:"event.service,handler:ResolveService" event:"*"` // SECLDoc[event.service] Definition:`Service associated with the event`
   131  
   132  	// context shared with all events
   133  	ProcessContext         *ProcessContext        `field:"process" event:"*"`
   134  	ContainerContext       *ContainerContext      `field:"container" event:"*"`
   135  	SecurityProfileContext SecurityProfileContext `field:"-"`
   136  
   137  	// internal usage
   138  	PIDContext        PIDContext         `field:"-"`
   139  	ProcessCacheEntry *ProcessCacheEntry `field:"-"`
   140  
   141  	// mark event with having error
   142  	Error error `field:"-"`
   143  
   144  	// field resolution
   145  	FieldHandlers FieldHandlers `field:"-"`
   146  }
   147  
   148  func initMember(member reflect.Value, deja map[string]bool) {
   149  	for i := 0; i < member.NumField(); i++ {
   150  		field := member.Field(i)
   151  
   152  		switch field.Kind() {
   153  		case reflect.Ptr:
   154  			if field.CanSet() {
   155  				field.Set(reflect.New(field.Type().Elem()))
   156  			}
   157  			if field.Elem().Kind() == reflect.Struct {
   158  				name := field.Elem().Type().Name()
   159  				if deja[name] {
   160  					continue
   161  				}
   162  				deja[name] = true
   163  
   164  				initMember(field.Elem(), deja)
   165  			}
   166  		case reflect.Struct:
   167  			name := field.Type().Name()
   168  			if deja[name] {
   169  				continue
   170  			}
   171  			deja[name] = true
   172  
   173  			initMember(field, deja)
   174  		}
   175  	}
   176  }
   177  
   178  // NewFakeEvent returns a new event using the default field handlers
   179  func NewFakeEvent() *Event {
   180  	return &Event{
   181  		BaseEvent: BaseEvent{
   182  			FieldHandlers:    &FakeFieldHandlers{},
   183  			ContainerContext: &ContainerContext{},
   184  			Os:               runtime.GOOS,
   185  		},
   186  	}
   187  }
   188  
   189  // Init initialize the event
   190  func (e *Event) Init() {
   191  	initMember(reflect.ValueOf(e).Elem(), map[string]bool{})
   192  }
   193  
   194  // Zero the event
   195  func (e *Event) Zero() {
   196  	*e = eventZero
   197  	*e.BaseEvent.ContainerContext = containerContextZero
   198  }
   199  
   200  // IsSavedByActivityDumps return whether saved by AD
   201  func (e *Event) IsSavedByActivityDumps() bool {
   202  	return e.Flags&EventFlagsSavedByAD > 0
   203  }
   204  
   205  // IsActivityDumpSample return whether AD sample
   206  func (e *Event) IsActivityDumpSample() bool {
   207  	return e.Flags&EventFlagsActivityDumpSample > 0
   208  }
   209  
   210  // IsInProfile return true if the event was found in the profile
   211  func (e *Event) IsInProfile() bool {
   212  	return e.Flags&EventFlagsSecurityProfileInProfile > 0
   213  }
   214  
   215  // HasActiveActivityDump returns true if the event has an active activity dump associated to it
   216  func (e *Event) HasActiveActivityDump() bool {
   217  	return e.Flags&EventFlagsHasActiveActivityDump > 0
   218  }
   219  
   220  // IsAnomalyDetectionEvent returns true if the current event is an anomaly detection event (kernel or user space)
   221  func (e *Event) IsAnomalyDetectionEvent() bool {
   222  	return e.Flags&EventFlagsAnomalyDetectionEvent > 0
   223  }
   224  
   225  // IsKernelSpaceAnomalyDetectionEvent returns true if the event is a kernel space anomaly detection event
   226  func (e *Event) IsKernelSpaceAnomalyDetectionEvent() bool {
   227  	return AnomalyDetectionSyscallEventType == e.GetEventType()
   228  }
   229  
   230  // AddToFlags adds a flag to the event
   231  func (e *Event) AddToFlags(flag uint32) {
   232  	e.Flags |= flag
   233  }
   234  
   235  // RemoveFromFlags remove a flag to the event
   236  func (e *Event) RemoveFromFlags(flag uint32) {
   237  	e.Flags ^= flag
   238  }
   239  
   240  // GetType returns the event type
   241  func (e *Event) GetType() string {
   242  	return EventType(e.Type).String()
   243  }
   244  
   245  // GetEventType returns the event type of the event
   246  func (e *Event) GetEventType() EventType {
   247  	return EventType(e.Type)
   248  }
   249  
   250  // GetTags returns the list of tags specific to this event
   251  func (e *Event) GetTags() []string {
   252  	tags := []string{"type:" + e.GetType()}
   253  
   254  	// should already be resolved at this stage
   255  	if len(e.ContainerContext.Tags) > 0 {
   256  		tags = append(tags, e.ContainerContext.Tags...)
   257  	}
   258  	return tags
   259  }
   260  
   261  // GetActionReports returns the triggred action reports
   262  func (e *Event) GetActionReports() []ActionReport {
   263  	return e.ActionReports
   264  }
   265  
   266  // GetWorkloadID returns an ID that represents the workload
   267  func (e *Event) GetWorkloadID() string {
   268  	return e.SecurityProfileContext.Name
   269  }
   270  
   271  // Retain the event
   272  func (e *Event) Retain() Event {
   273  	if e.ProcessCacheEntry != nil {
   274  		e.ProcessCacheEntry.Retain()
   275  	}
   276  	return *e
   277  }
   278  
   279  // Release the event
   280  func (e *Event) Release() {
   281  	if e.ProcessCacheEntry != nil {
   282  		e.ProcessCacheEntry.Release()
   283  	}
   284  }
   285  
   286  // ResolveProcessCacheEntry uses the field handler
   287  func (e *Event) ResolveProcessCacheEntry() (*ProcessCacheEntry, bool) {
   288  	return e.FieldHandlers.ResolveProcessCacheEntry(e)
   289  }
   290  
   291  // ResolveEventTime uses the field handler
   292  func (e *Event) ResolveEventTime() time.Time {
   293  	return e.FieldHandlers.ResolveEventTime(e, &e.BaseEvent)
   294  }
   295  
   296  // ResolveService uses the field handler
   297  func (e *Event) ResolveService() string {
   298  	return e.FieldHandlers.ResolveService(e, &e.BaseEvent)
   299  }
   300  
   301  // UserSessionContext describes the user session context
   302  // Disclaimer: the `json` tags are used to parse K8s credentials from cws-instrumentation
   303  type UserSessionContext struct {
   304  	ID          uint64           `field:"-"`
   305  	SessionType usersession.Type `field:"-"`
   306  	Resolved    bool             `field:"-"`
   307  	// Kubernetes User Session context
   308  	K8SUsername string              `field:"k8s_username,handler:ResolveK8SUsername" json:"username,omitempty"` // SECLDoc[k8s_username] Definition:`Kubernetes username of the user that executed the process`
   309  	K8SUID      string              `field:"k8s_uid,handler:ResolveK8SUID" json:"uid,omitempty"`                // SECLDoc[k8s_uid] Definition:`Kubernetes UID of the user that executed the process`
   310  	K8SGroups   []string            `field:"k8s_groups,handler:ResolveK8SGroups" json:"groups,omitempty"`       // SECLDoc[k8s_groups] Definition:`Kubernetes groups of the user that executed the process`
   311  	K8SExtra    map[string][]string `json:"extra,omitempty"`
   312  }
   313  
   314  // MatchedRule contains the identification of one rule that has match
   315  type MatchedRule struct {
   316  	RuleID        string
   317  	RuleVersion   string
   318  	RuleTags      map[string]string
   319  	PolicyName    string
   320  	PolicyVersion string
   321  }
   322  
   323  // ActionReport defines an action report
   324  type ActionReport interface {
   325  	ToJSON() ([]byte, bool, error)
   326  }
   327  
   328  // NewMatchedRule return a new MatchedRule instance
   329  func NewMatchedRule(ruleID, ruleVersion string, ruleTags map[string]string, policyName, policyVersion string) *MatchedRule {
   330  	return &MatchedRule{
   331  		RuleID:        ruleID,
   332  		RuleVersion:   ruleVersion,
   333  		RuleTags:      ruleTags,
   334  		PolicyName:    policyName,
   335  		PolicyVersion: policyVersion,
   336  	}
   337  }
   338  
   339  // Match returns true if the rules are equal
   340  func (mr *MatchedRule) Match(mr2 *MatchedRule) bool {
   341  	if mr2 == nil ||
   342  		mr.RuleID != mr2.RuleID ||
   343  		mr.RuleVersion != mr2.RuleVersion ||
   344  		mr.PolicyName != mr2.PolicyName ||
   345  		mr.PolicyVersion != mr2.PolicyVersion {
   346  		return false
   347  	}
   348  	return true
   349  }
   350  
   351  // AppendMatchedRule appends two lists, but avoiding duplicates
   352  func AppendMatchedRule(list []*MatchedRule, toAdd []*MatchedRule) []*MatchedRule {
   353  	for _, ta := range toAdd {
   354  		found := false
   355  		for _, l := range list {
   356  			if l.Match(ta) { // rule already present
   357  				found = true
   358  				break
   359  			}
   360  		}
   361  		if !found {
   362  			list = append(list, ta)
   363  		}
   364  	}
   365  	return list
   366  }
   367  
   368  // HashState is used to prevent the hash resolver from retrying to hash a file
   369  type HashState int
   370  
   371  const (
   372  	// NoHash means that computing a hash hasn't been attempted
   373  	NoHash HashState = iota
   374  	// Done means that the hashes were already computed
   375  	Done
   376  	// FileNotFound means that the underlying file is not longer available to compute the hash
   377  	FileNotFound
   378  	// PathnameResolutionError means that the underlying file wasn't properly resolved
   379  	PathnameResolutionError
   380  	// FileTooBig means that the underlying file is larger than the hash resolver file size limit
   381  	FileTooBig
   382  	// FileEmpty means that the underlying file is empty
   383  	FileEmpty
   384  	// FileOpenError is a generic hash state to say that we couldn't open the file
   385  	FileOpenError
   386  	// EventTypeNotConfigured means that the event type prevents a hash from being computed
   387  	EventTypeNotConfigured
   388  	// HashWasRateLimited means that the hash will be tried again later, it was rate limited
   389  	HashWasRateLimited
   390  	// HashFailed means that the hashing failed
   391  	HashFailed
   392  	// MaxHashState is used for initializations
   393  	MaxHashState
   394  )
   395  
   396  // HashAlgorithm is used to configure the hash algorithms of the hash resolver
   397  type HashAlgorithm int
   398  
   399  const (
   400  	// SHA1 is used to identify a SHA1 hash
   401  	SHA1 HashAlgorithm = iota
   402  	// SHA256 is used to identify a SHA256 hash
   403  	SHA256
   404  	// MD5 is used to identify a MD5 hash
   405  	MD5
   406  	// SSDEEP is used to identify a SSDEEP hash
   407  	SSDEEP
   408  	// MaxHashAlgorithm is used for initializations
   409  	MaxHashAlgorithm
   410  )
   411  
   412  func (ha HashAlgorithm) String() string {
   413  	switch ha {
   414  	case SHA1:
   415  		return "sha1"
   416  	case SHA256:
   417  		return "sha256"
   418  	case MD5:
   419  		return "md5"
   420  	case SSDEEP:
   421  		return "ssdeep"
   422  	default:
   423  		return ""
   424  	}
   425  }
   426  
   427  var zeroProcessContext ProcessContext
   428  
   429  // ProcessCacheEntry this struct holds process context kept in the process tree
   430  type ProcessCacheEntry struct {
   431  	ProcessContext
   432  
   433  	refCount  uint64                     `field:"-"`
   434  	onRelease func(_ *ProcessCacheEntry) `field:"-"`
   435  	releaseCb func()                     `field:"-"`
   436  }
   437  
   438  // IsContainerRoot returns whether this is a top level process in the container ID
   439  func (pc *ProcessCacheEntry) IsContainerRoot() bool {
   440  	return pc.ContainerID != "" && pc.Ancestor != nil && pc.Ancestor.ContainerID == ""
   441  }
   442  
   443  // Reset the entry
   444  func (pc *ProcessCacheEntry) Reset() {
   445  	pc.ProcessContext = zeroProcessContext
   446  	pc.refCount = 0
   447  	pc.releaseCb = nil
   448  }
   449  
   450  // Retain increment ref counter
   451  func (pc *ProcessCacheEntry) Retain() {
   452  	pc.refCount++
   453  }
   454  
   455  // SetReleaseCallback set the callback called when the entry is released
   456  func (pc *ProcessCacheEntry) SetReleaseCallback(callback func()) {
   457  	previousCallback := pc.releaseCb
   458  	pc.releaseCb = func() {
   459  		callback()
   460  		if previousCallback != nil {
   461  			previousCallback()
   462  		}
   463  	}
   464  }
   465  
   466  // Release decrement and eventually release the entry
   467  func (pc *ProcessCacheEntry) Release() {
   468  	pc.refCount--
   469  	if pc.refCount > 0 {
   470  		return
   471  	}
   472  
   473  	if pc.onRelease != nil {
   474  		pc.onRelease(pc)
   475  	}
   476  
   477  	if pc.releaseCb != nil {
   478  		pc.releaseCb()
   479  	}
   480  }
   481  
   482  // NewProcessCacheEntry returns a new process cache entry
   483  func NewProcessCacheEntry(onRelease func(_ *ProcessCacheEntry)) *ProcessCacheEntry {
   484  	return &ProcessCacheEntry{onRelease: onRelease}
   485  }
   486  
   487  // ProcessAncestorsIterator defines an iterator of ancestors
   488  type ProcessAncestorsIterator struct {
   489  	prev *ProcessCacheEntry
   490  }
   491  
   492  // Front returns the first element
   493  func (it *ProcessAncestorsIterator) Front(ctx *eval.Context) unsafe.Pointer {
   494  	if front := ctx.Event.(*Event).ProcessContext.Ancestor; front != nil {
   495  		it.prev = front
   496  		return unsafe.Pointer(front)
   497  	}
   498  
   499  	return nil
   500  }
   501  
   502  // Next returns the next element
   503  func (it *ProcessAncestorsIterator) Next() unsafe.Pointer {
   504  	if next := it.prev.Ancestor; next != nil {
   505  		it.prev = next
   506  		return unsafe.Pointer(next)
   507  	}
   508  
   509  	return nil
   510  }
   511  
   512  // HasParent returns whether the process has a parent
   513  func (p *ProcessContext) HasParent() bool {
   514  	return p.Parent != nil
   515  }
   516  
   517  // ProcessContext holds the process context of an event
   518  type ProcessContext struct {
   519  	Process
   520  
   521  	Parent   *Process           `field:"parent,opts:exposed_at_event_root_only,check:HasParent"`
   522  	Ancestor *ProcessCacheEntry `field:"ancestors,iterator:ProcessAncestorsIterator,check:IsNotKworker"`
   523  }
   524  
   525  // ExitEvent represents a process exit event
   526  type ExitEvent struct {
   527  	*Process
   528  	Cause uint32 `field:"cause"` // SECLDoc[cause] Definition:`Cause of the process termination (one of EXITED, SIGNALED, COREDUMPED)`
   529  	Code  uint32 `field:"code"`  // SECLDoc[code] Definition:`Exit code of the process or number of the signal that caused the process to terminate`
   530  }
   531  
   532  // DNSEvent represents a DNS event
   533  type DNSEvent struct {
   534  	ID    uint16 `field:"id"`                                                              // SECLDoc[id] Definition:`[Experimental] the DNS request ID`
   535  	Name  string `field:"question.name,opts:length" op_override:"eval.CaseInsensitiveCmp"` // SECLDoc[question.name] Definition:`the queried domain name`
   536  	Type  uint16 `field:"question.type"`                                                   // SECLDoc[question.type] Definition:`a two octet code which specifies the DNS question type` Constants:`DNS qtypes`
   537  	Class uint16 `field:"question.class"`                                                  // SECLDoc[question.class] Definition:`the class looked up by the DNS question` Constants:`DNS qclasses`
   538  	Size  uint16 `field:"question.length"`                                                 // SECLDoc[question.length] Definition:`the total DNS request size in bytes`
   539  	Count uint16 `field:"question.count"`                                                  // SECLDoc[question.count] Definition:`the total count of questions in the DNS request`
   540  }
   541  
   542  // Matches returns true if the two DNS events matches
   543  func (de *DNSEvent) Matches(new *DNSEvent) bool {
   544  	return de.Name == new.Name && de.Type == new.Type && de.Class == new.Class
   545  }
   546  
   547  // BaseExtraFieldHandlers handlers not hold by any field
   548  type BaseExtraFieldHandlers interface {
   549  	ResolveProcessCacheEntry(ev *Event) (*ProcessCacheEntry, bool)
   550  	ResolveContainerContext(ev *Event) (*ContainerContext, bool)
   551  }
   552  
   553  // ResolveProcessCacheEntry stub implementation
   554  func (dfh *FakeFieldHandlers) ResolveProcessCacheEntry(_ *Event) (*ProcessCacheEntry, bool) {
   555  	return nil, false
   556  }
   557  
   558  // ResolveContainerContext stub implementation
   559  func (dfh *FakeFieldHandlers) ResolveContainerContext(_ *Event) (*ContainerContext, bool) {
   560  	return nil, false
   561  }