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 }