1// +build linux 2 3// Public API specification for libseccomp Go bindings 4// Contains public API for the bindings 5 6// Package seccomp provides bindings for libseccomp, a library wrapping the Linux 7// seccomp syscall. Seccomp enables an application to restrict system call use 8// for itself and its children. 9package seccomp 10 11import ( 12 "fmt" 13 "os" 14 "runtime" 15 "strings" 16 "sync" 17 "syscall" 18 "unsafe" 19) 20 21// C wrapping code 22 23// #cgo pkg-config: libseccomp 24// #include <stdlib.h> 25// #include <seccomp.h> 26import "C" 27 28// Exported types 29 30// VersionError denotes that the system libseccomp version is incompatible 31// with this package. 32type VersionError struct { 33 message string 34 minimum string 35} 36 37func (e VersionError) Error() string { 38 format := "Libseccomp version too low: " 39 if e.message != "" { 40 format += e.message + ": " 41 } 42 format += "minimum supported is " 43 if e.minimum != "" { 44 format += e.minimum + ": " 45 } else { 46 format += "2.2.0: " 47 } 48 format += "detected %d.%d.%d" 49 return fmt.Sprintf(format, verMajor, verMinor, verMicro) 50} 51 52// ScmpArch represents a CPU architecture. Seccomp can restrict syscalls on a 53// per-architecture basis. 54type ScmpArch uint 55 56// ScmpAction represents an action to be taken on a filter rule match in 57// libseccomp 58type ScmpAction uint 59 60// ScmpCompareOp represents a comparison operator which can be used in a filter 61// rule 62type ScmpCompareOp uint 63 64// ScmpCondition represents a rule in a libseccomp filter context 65type ScmpCondition struct { 66 Argument uint `json:"argument,omitempty"` 67 Op ScmpCompareOp `json:"operator,omitempty"` 68 Operand1 uint64 `json:"operand_one,omitempty"` 69 Operand2 uint64 `json:"operand_two,omitempty"` 70} 71 72// ScmpSyscall represents a Linux System Call 73type ScmpSyscall int32 74 75// Exported Constants 76 77const ( 78 // Valid architectures recognized by libseccomp 79 // PowerPC and S390(x) architectures are unavailable below library version 80 // v2.3.0 and will returns errors if used with incompatible libraries 81 82 // ArchInvalid is a placeholder to ensure uninitialized ScmpArch 83 // variables are invalid 84 ArchInvalid ScmpArch = iota 85 // ArchNative is the native architecture of the kernel 86 ArchNative ScmpArch = iota 87 // ArchX86 represents 32-bit x86 syscalls 88 ArchX86 ScmpArch = iota 89 // ArchAMD64 represents 64-bit x86-64 syscalls 90 ArchAMD64 ScmpArch = iota 91 // ArchX32 represents 64-bit x86-64 syscalls (32-bit pointers) 92 ArchX32 ScmpArch = iota 93 // ArchARM represents 32-bit ARM syscalls 94 ArchARM ScmpArch = iota 95 // ArchARM64 represents 64-bit ARM syscalls 96 ArchARM64 ScmpArch = iota 97 // ArchMIPS represents 32-bit MIPS syscalls 98 ArchMIPS ScmpArch = iota 99 // ArchMIPS64 represents 64-bit MIPS syscalls 100 ArchMIPS64 ScmpArch = iota 101 // ArchMIPS64N32 represents 64-bit MIPS syscalls (32-bit pointers) 102 ArchMIPS64N32 ScmpArch = iota 103 // ArchMIPSEL represents 32-bit MIPS syscalls (little endian) 104 ArchMIPSEL ScmpArch = iota 105 // ArchMIPSEL64 represents 64-bit MIPS syscalls (little endian) 106 ArchMIPSEL64 ScmpArch = iota 107 // ArchMIPSEL64N32 represents 64-bit MIPS syscalls (little endian, 108 // 32-bit pointers) 109 ArchMIPSEL64N32 ScmpArch = iota 110 // ArchPPC represents 32-bit POWERPC syscalls 111 ArchPPC ScmpArch = iota 112 // ArchPPC64 represents 64-bit POWER syscalls (big endian) 113 ArchPPC64 ScmpArch = iota 114 // ArchPPC64LE represents 64-bit POWER syscalls (little endian) 115 ArchPPC64LE ScmpArch = iota 116 // ArchS390 represents 31-bit System z/390 syscalls 117 ArchS390 ScmpArch = iota 118 // ArchS390X represents 64-bit System z/390 syscalls 119 ArchS390X ScmpArch = iota 120) 121 122const ( 123 // Supported actions on filter match 124 125 // ActInvalid is a placeholder to ensure uninitialized ScmpAction 126 // variables are invalid 127 ActInvalid ScmpAction = iota 128 // ActKill kills the process 129 ActKill ScmpAction = iota 130 // ActTrap throws SIGSYS 131 ActTrap ScmpAction = iota 132 // ActErrno causes the syscall to return a negative error code. This 133 // code can be set with the SetReturnCode method 134 ActErrno ScmpAction = iota 135 // ActTrace causes the syscall to notify tracing processes with the 136 // given error code. This code can be set with the SetReturnCode method 137 ActTrace ScmpAction = iota 138 // ActAllow permits the syscall to continue execution 139 ActAllow ScmpAction = iota 140 // ActLog permits the syscall to continue execution after logging it. 141 // This action is only usable when libseccomp API level 3 or higher is 142 // supported. 143 ActLog ScmpAction = iota 144) 145 146const ( 147 // These are comparison operators used in conditional seccomp rules 148 // They are used to compare the value of a single argument of a syscall 149 // against a user-defined constant 150 151 // CompareInvalid is a placeholder to ensure uninitialized ScmpCompareOp 152 // variables are invalid 153 CompareInvalid ScmpCompareOp = iota 154 // CompareNotEqual returns true if the argument is not equal to the 155 // given value 156 CompareNotEqual ScmpCompareOp = iota 157 // CompareLess returns true if the argument is less than the given value 158 CompareLess ScmpCompareOp = iota 159 // CompareLessOrEqual returns true if the argument is less than or equal 160 // to the given value 161 CompareLessOrEqual ScmpCompareOp = iota 162 // CompareEqual returns true if the argument is equal to the given value 163 CompareEqual ScmpCompareOp = iota 164 // CompareGreaterEqual returns true if the argument is greater than or 165 // equal to the given value 166 CompareGreaterEqual ScmpCompareOp = iota 167 // CompareGreater returns true if the argument is greater than the given 168 // value 169 CompareGreater ScmpCompareOp = iota 170 // CompareMaskedEqual returns true if the argument is equal to the given 171 // value, when masked (bitwise &) against the second given value 172 CompareMaskedEqual ScmpCompareOp = iota 173) 174 175// Helpers for types 176 177// GetArchFromString returns an ScmpArch constant from a string representing an 178// architecture 179func GetArchFromString(arch string) (ScmpArch, error) { 180 if err := ensureSupportedVersion(); err != nil { 181 return ArchInvalid, err 182 } 183 184 switch strings.ToLower(arch) { 185 case "x86": 186 return ArchX86, nil 187 case "amd64", "x86-64", "x86_64", "x64": 188 return ArchAMD64, nil 189 case "x32": 190 return ArchX32, nil 191 case "arm": 192 return ArchARM, nil 193 case "arm64", "aarch64": 194 return ArchARM64, nil 195 case "mips": 196 return ArchMIPS, nil 197 case "mips64": 198 return ArchMIPS64, nil 199 case "mips64n32": 200 return ArchMIPS64N32, nil 201 case "mipsel": 202 return ArchMIPSEL, nil 203 case "mipsel64": 204 return ArchMIPSEL64, nil 205 case "mipsel64n32": 206 return ArchMIPSEL64N32, nil 207 case "ppc": 208 return ArchPPC, nil 209 case "ppc64": 210 return ArchPPC64, nil 211 case "ppc64le": 212 return ArchPPC64LE, nil 213 case "s390": 214 return ArchS390, nil 215 case "s390x": 216 return ArchS390X, nil 217 default: 218 return ArchInvalid, fmt.Errorf("cannot convert unrecognized string %q", arch) 219 } 220} 221 222// String returns a string representation of an architecture constant 223func (a ScmpArch) String() string { 224 switch a { 225 case ArchX86: 226 return "x86" 227 case ArchAMD64: 228 return "amd64" 229 case ArchX32: 230 return "x32" 231 case ArchARM: 232 return "arm" 233 case ArchARM64: 234 return "arm64" 235 case ArchMIPS: 236 return "mips" 237 case ArchMIPS64: 238 return "mips64" 239 case ArchMIPS64N32: 240 return "mips64n32" 241 case ArchMIPSEL: 242 return "mipsel" 243 case ArchMIPSEL64: 244 return "mipsel64" 245 case ArchMIPSEL64N32: 246 return "mipsel64n32" 247 case ArchPPC: 248 return "ppc" 249 case ArchPPC64: 250 return "ppc64" 251 case ArchPPC64LE: 252 return "ppc64le" 253 case ArchS390: 254 return "s390" 255 case ArchS390X: 256 return "s390x" 257 case ArchNative: 258 return "native" 259 case ArchInvalid: 260 return "Invalid architecture" 261 default: 262 return fmt.Sprintf("Unknown architecture %#x", uint(a)) 263 } 264} 265 266// String returns a string representation of a comparison operator constant 267func (a ScmpCompareOp) String() string { 268 switch a { 269 case CompareNotEqual: 270 return "Not equal" 271 case CompareLess: 272 return "Less than" 273 case CompareLessOrEqual: 274 return "Less than or equal to" 275 case CompareEqual: 276 return "Equal" 277 case CompareGreaterEqual: 278 return "Greater than or equal to" 279 case CompareGreater: 280 return "Greater than" 281 case CompareMaskedEqual: 282 return "Masked equality" 283 case CompareInvalid: 284 return "Invalid comparison operator" 285 default: 286 return fmt.Sprintf("Unrecognized comparison operator %#x", uint(a)) 287 } 288} 289 290// String returns a string representation of a seccomp match action 291func (a ScmpAction) String() string { 292 switch a & 0xFFFF { 293 case ActKill: 294 return "Action: Kill Process" 295 case ActTrap: 296 return "Action: Send SIGSYS" 297 case ActErrno: 298 return fmt.Sprintf("Action: Return error code %d", (a >> 16)) 299 case ActTrace: 300 return fmt.Sprintf("Action: Notify tracing processes with code %d", 301 (a >> 16)) 302 case ActLog: 303 return "Action: Log system call" 304 case ActAllow: 305 return "Action: Allow system call" 306 default: 307 return fmt.Sprintf("Unrecognized Action %#x", uint(a)) 308 } 309} 310 311// SetReturnCode adds a return code to a supporting ScmpAction, clearing any 312// existing code Only valid on ActErrno and ActTrace. Takes no action otherwise. 313// Accepts 16-bit return code as argument. 314// Returns a valid ScmpAction of the original type with the new error code set. 315func (a ScmpAction) SetReturnCode(code int16) ScmpAction { 316 aTmp := a & 0x0000FFFF 317 if aTmp == ActErrno || aTmp == ActTrace { 318 return (aTmp | (ScmpAction(code)&0xFFFF)<<16) 319 } 320 return a 321} 322 323// GetReturnCode returns the return code of an ScmpAction 324func (a ScmpAction) GetReturnCode() int16 { 325 return int16(a >> 16) 326} 327 328// General utility functions 329 330// GetLibraryVersion returns the version of the library the bindings are built 331// against. 332// The version is formatted as follows: Major.Minor.Micro 333func GetLibraryVersion() (major, minor, micro uint) { 334 return verMajor, verMinor, verMicro 335} 336 337// GetApi returns the API level supported by the system. 338// Returns a positive int containing the API level, or 0 with an error if the 339// API level could not be detected due to the library being older than v2.4.0. 340// See the seccomp_api_get(3) man page for details on available API levels: 341// https://github.com/seccomp/libseccomp/blob/master/doc/man/man3/seccomp_api_get.3 342func GetApi() (uint, error) { 343 return getApi() 344} 345 346// SetApi forcibly sets the API level. General use of this function is strongly 347// discouraged. 348// Returns an error if the API level could not be set. An error is always 349// returned if the library is older than v2.4.0 350// See the seccomp_api_get(3) man page for details on available API levels: 351// https://github.com/seccomp/libseccomp/blob/master/doc/man/man3/seccomp_api_get.3 352func SetApi(api uint) error { 353 return setApi(api) 354} 355 356// Syscall functions 357 358// GetName retrieves the name of a syscall from its number. 359// Acts on any syscall number. 360// Returns either a string containing the name of the syscall, or an error. 361func (s ScmpSyscall) GetName() (string, error) { 362 return s.GetNameByArch(ArchNative) 363} 364 365// GetNameByArch retrieves the name of a syscall from its number for a given 366// architecture. 367// Acts on any syscall number. 368// Accepts a valid architecture constant. 369// Returns either a string containing the name of the syscall, or an error. 370// if the syscall is unrecognized or an issue occurred. 371func (s ScmpSyscall) GetNameByArch(arch ScmpArch) (string, error) { 372 if err := sanitizeArch(arch); err != nil { 373 return "", err 374 } 375 376 cString := C.seccomp_syscall_resolve_num_arch(arch.toNative(), C.int(s)) 377 if cString == nil { 378 return "", fmt.Errorf("could not resolve syscall name for %#x", int32(s)) 379 } 380 defer C.free(unsafe.Pointer(cString)) 381 382 finalStr := C.GoString(cString) 383 return finalStr, nil 384} 385 386// GetSyscallFromName returns the number of a syscall by name on the kernel's 387// native architecture. 388// Accepts a string containing the name of a syscall. 389// Returns the number of the syscall, or an error if no syscall with that name 390// was found. 391func GetSyscallFromName(name string) (ScmpSyscall, error) { 392 if err := ensureSupportedVersion(); err != nil { 393 return 0, err 394 } 395 396 cString := C.CString(name) 397 defer C.free(unsafe.Pointer(cString)) 398 399 result := C.seccomp_syscall_resolve_name(cString) 400 if result == scmpError { 401 return 0, fmt.Errorf("could not resolve name to syscall: %q", name) 402 } 403 404 return ScmpSyscall(result), nil 405} 406 407// GetSyscallFromNameByArch returns the number of a syscall by name for a given 408// architecture's ABI. 409// Accepts the name of a syscall and an architecture constant. 410// Returns the number of the syscall, or an error if an invalid architecture is 411// passed or a syscall with that name was not found. 412func GetSyscallFromNameByArch(name string, arch ScmpArch) (ScmpSyscall, error) { 413 if err := ensureSupportedVersion(); err != nil { 414 return 0, err 415 } 416 if err := sanitizeArch(arch); err != nil { 417 return 0, err 418 } 419 420 cString := C.CString(name) 421 defer C.free(unsafe.Pointer(cString)) 422 423 result := C.seccomp_syscall_resolve_name_arch(arch.toNative(), cString) 424 if result == scmpError { 425 return 0, fmt.Errorf("could not resolve name to syscall: %q on %v", name, arch) 426 } 427 428 return ScmpSyscall(result), nil 429} 430 431// MakeCondition creates and returns a new condition to attach to a filter rule. 432// Associated rules will only match if this condition is true. 433// Accepts the number the argument we are checking, and a comparison operator 434// and value to compare to. 435// The rule will match if argument $arg (zero-indexed) of the syscall is 436// $COMPARE_OP the provided comparison value. 437// Some comparison operators accept two values. Masked equals, for example, 438// will mask $arg of the syscall with the second value provided (via bitwise 439// AND) and then compare against the first value provided. 440// For example, in the less than or equal case, if the syscall argument was 441// 0 and the value provided was 1, the condition would match, as 0 is less 442// than or equal to 1. 443// Return either an error on bad argument or a valid ScmpCondition struct. 444func MakeCondition(arg uint, comparison ScmpCompareOp, values ...uint64) (ScmpCondition, error) { 445 var condStruct ScmpCondition 446 447 if err := ensureSupportedVersion(); err != nil { 448 return condStruct, err 449 } 450 451 if comparison == CompareInvalid { 452 return condStruct, fmt.Errorf("invalid comparison operator") 453 } else if arg > 5 { 454 return condStruct, fmt.Errorf("syscalls only have up to 6 arguments (%d given)", arg) 455 } else if len(values) > 2 { 456 return condStruct, fmt.Errorf("conditions can have at most 2 arguments (%d given)", len(values)) 457 } else if len(values) == 0 { 458 return condStruct, fmt.Errorf("must provide at least one value to compare against") 459 } 460 461 condStruct.Argument = arg 462 condStruct.Op = comparison 463 condStruct.Operand1 = values[0] 464 if len(values) == 2 { 465 condStruct.Operand2 = values[1] 466 } else { 467 condStruct.Operand2 = 0 // Unused 468 } 469 470 return condStruct, nil 471} 472 473// Utility Functions 474 475// GetNativeArch returns architecture token representing the native kernel 476// architecture 477func GetNativeArch() (ScmpArch, error) { 478 if err := ensureSupportedVersion(); err != nil { 479 return ArchInvalid, err 480 } 481 482 arch := C.seccomp_arch_native() 483 484 return archFromNative(arch) 485} 486 487// Public Filter API 488 489// ScmpFilter represents a filter context in libseccomp. 490// A filter context is initially empty. Rules can be added to it, and it can 491// then be loaded into the kernel. 492type ScmpFilter struct { 493 filterCtx C.scmp_filter_ctx 494 valid bool 495 lock sync.Mutex 496} 497 498// NewFilter creates and returns a new filter context. 499// Accepts a default action to be taken for syscalls which match no rules in 500// the filter. 501// Returns a reference to a valid filter context, or nil and an error if the 502// filter context could not be created or an invalid default action was given. 503func NewFilter(defaultAction ScmpAction) (*ScmpFilter, error) { 504 if err := ensureSupportedVersion(); err != nil { 505 return nil, err 506 } 507 508 if err := sanitizeAction(defaultAction); err != nil { 509 return nil, err 510 } 511 512 fPtr := C.seccomp_init(defaultAction.toNative()) 513 if fPtr == nil { 514 return nil, fmt.Errorf("could not create filter") 515 } 516 517 filter := new(ScmpFilter) 518 filter.filterCtx = fPtr 519 filter.valid = true 520 runtime.SetFinalizer(filter, filterFinalizer) 521 522 // Enable TSync so all goroutines will receive the same rules 523 // If the kernel does not support TSYNC, allow us to continue without error 524 if err := filter.setFilterAttr(filterAttrTsync, 0x1); err != nil && err != syscall.ENOTSUP { 525 filter.Release() 526 return nil, fmt.Errorf("could not create filter - error setting tsync bit: %v", err) 527 } 528 529 return filter, nil 530} 531 532// IsValid determines whether a filter context is valid to use. 533// Some operations (Release and Merge) render filter contexts invalid and 534// consequently prevent further use. 535func (f *ScmpFilter) IsValid() bool { 536 f.lock.Lock() 537 defer f.lock.Unlock() 538 539 return f.valid 540} 541 542// Reset resets a filter context, removing all its existing state. 543// Accepts a new default action to be taken for syscalls which do not match. 544// Returns an error if the filter or action provided are invalid. 545func (f *ScmpFilter) Reset(defaultAction ScmpAction) error { 546 f.lock.Lock() 547 defer f.lock.Unlock() 548 549 if err := sanitizeAction(defaultAction); err != nil { 550 return err 551 } else if !f.valid { 552 return errBadFilter 553 } 554 555 retCode := C.seccomp_reset(f.filterCtx, defaultAction.toNative()) 556 if retCode != 0 { 557 return syscall.Errno(-1 * retCode) 558 } 559 560 return nil 561} 562 563// Release releases a filter context, freeing its memory. Should be called after 564// loading into the kernel, when the filter is no longer needed. 565// After calling this function, the given filter is no longer valid and cannot 566// be used. 567// Release() will be invoked automatically when a filter context is garbage 568// collected, but can also be called manually to free memory. 569func (f *ScmpFilter) Release() { 570 f.lock.Lock() 571 defer f.lock.Unlock() 572 573 if !f.valid { 574 return 575 } 576 577 f.valid = false 578 C.seccomp_release(f.filterCtx) 579} 580 581// Merge merges two filter contexts. 582// The source filter src will be released as part of the process, and will no 583// longer be usable or valid after this call. 584// To be merged, filters must NOT share any architectures, and all their 585// attributes (Default Action, Bad Arch Action, and No New Privs bools) 586// must match. 587// The filter src will be merged into the filter this is called on. 588// The architectures of the src filter not present in the destination, and all 589// associated rules, will be added to the destination. 590// Returns an error if merging the filters failed. 591func (f *ScmpFilter) Merge(src *ScmpFilter) error { 592 f.lock.Lock() 593 defer f.lock.Unlock() 594 595 src.lock.Lock() 596 defer src.lock.Unlock() 597 598 if !src.valid || !f.valid { 599 return fmt.Errorf("one or more of the filter contexts is invalid or uninitialized") 600 } 601 602 // Merge the filters 603 retCode := C.seccomp_merge(f.filterCtx, src.filterCtx) 604 if syscall.Errno(-1*retCode) == syscall.EINVAL { 605 return fmt.Errorf("filters could not be merged due to a mismatch in attributes or invalid filter") 606 } else if retCode != 0 { 607 return syscall.Errno(-1 * retCode) 608 } 609 610 src.valid = false 611 612 return nil 613} 614 615// IsArchPresent checks if an architecture is present in a filter. 616// If a filter contains an architecture, it uses its default action for 617// syscalls which do not match rules in it, and its rules can match syscalls 618// for that ABI. 619// If a filter does not contain an architecture, all syscalls made to that 620// kernel ABI will fail with the filter's default Bad Architecture Action 621// (by default, killing the process). 622// Accepts an architecture constant. 623// Returns true if the architecture is present in the filter, false otherwise, 624// and an error on an invalid filter context, architecture constant, or an 625// issue with the call to libseccomp. 626func (f *ScmpFilter) IsArchPresent(arch ScmpArch) (bool, error) { 627 f.lock.Lock() 628 defer f.lock.Unlock() 629 630 if err := sanitizeArch(arch); err != nil { 631 return false, err 632 } else if !f.valid { 633 return false, errBadFilter 634 } 635 636 retCode := C.seccomp_arch_exist(f.filterCtx, arch.toNative()) 637 if syscall.Errno(-1*retCode) == syscall.EEXIST { 638 // -EEXIST is "arch not present" 639 return false, nil 640 } else if retCode != 0 { 641 return false, syscall.Errno(-1 * retCode) 642 } 643 644 return true, nil 645} 646 647// AddArch adds an architecture to the filter. 648// Accepts an architecture constant. 649// Returns an error on invalid filter context or architecture token, or an 650// issue with the call to libseccomp. 651func (f *ScmpFilter) AddArch(arch ScmpArch) error { 652 f.lock.Lock() 653 defer f.lock.Unlock() 654 655 if err := sanitizeArch(arch); err != nil { 656 return err 657 } else if !f.valid { 658 return errBadFilter 659 } 660 661 // Libseccomp returns -EEXIST if the specified architecture is already 662 // present. Succeed silently in this case, as it's not fatal, and the 663 // architecture is present already. 664 retCode := C.seccomp_arch_add(f.filterCtx, arch.toNative()) 665 if retCode != 0 && syscall.Errno(-1*retCode) != syscall.EEXIST { 666 return syscall.Errno(-1 * retCode) 667 } 668 669 return nil 670} 671 672// RemoveArch removes an architecture from the filter. 673// Accepts an architecture constant. 674// Returns an error on invalid filter context or architecture token, or an 675// issue with the call to libseccomp. 676func (f *ScmpFilter) RemoveArch(arch ScmpArch) error { 677 f.lock.Lock() 678 defer f.lock.Unlock() 679 680 if err := sanitizeArch(arch); err != nil { 681 return err 682 } else if !f.valid { 683 return errBadFilter 684 } 685 686 // Similar to AddArch, -EEXIST is returned if the arch is not present 687 // Succeed silently in that case, this is not fatal and the architecture 688 // is not present in the filter after RemoveArch 689 retCode := C.seccomp_arch_remove(f.filterCtx, arch.toNative()) 690 if retCode != 0 && syscall.Errno(-1*retCode) != syscall.EEXIST { 691 return syscall.Errno(-1 * retCode) 692 } 693 694 return nil 695} 696 697// Load loads a filter context into the kernel. 698// Returns an error if the filter context is invalid or the syscall failed. 699func (f *ScmpFilter) Load() error { 700 f.lock.Lock() 701 defer f.lock.Unlock() 702 703 if !f.valid { 704 return errBadFilter 705 } 706 707 if retCode := C.seccomp_load(f.filterCtx); retCode != 0 { 708 return syscall.Errno(-1 * retCode) 709 } 710 711 return nil 712} 713 714// GetDefaultAction returns the default action taken on a syscall which does not 715// match a rule in the filter, or an error if an issue was encountered 716// retrieving the value. 717func (f *ScmpFilter) GetDefaultAction() (ScmpAction, error) { 718 action, err := f.getFilterAttr(filterAttrActDefault) 719 if err != nil { 720 return 0x0, err 721 } 722 723 return actionFromNative(action) 724} 725 726// GetBadArchAction returns the default action taken on a syscall for an 727// architecture not in the filter, or an error if an issue was encountered 728// retrieving the value. 729func (f *ScmpFilter) GetBadArchAction() (ScmpAction, error) { 730 action, err := f.getFilterAttr(filterAttrActBadArch) 731 if err != nil { 732 return 0x0, err 733 } 734 735 return actionFromNative(action) 736} 737 738// GetNoNewPrivsBit returns the current state the No New Privileges bit will be set 739// to on the filter being loaded, or an error if an issue was encountered 740// retrieving the value. 741// The No New Privileges bit tells the kernel that new processes run with exec() 742// cannot gain more privileges than the process that ran exec(). 743// For example, a process with No New Privileges set would be unable to exec 744// setuid/setgid executables. 745func (f *ScmpFilter) GetNoNewPrivsBit() (bool, error) { 746 noNewPrivs, err := f.getFilterAttr(filterAttrNNP) 747 if err != nil { 748 return false, err 749 } 750 751 if noNewPrivs == 0 { 752 return false, nil 753 } 754 755 return true, nil 756} 757 758// GetLogBit returns the current state the Log bit will be set to on the filter 759// being loaded, or an error if an issue was encountered retrieving the value. 760// The Log bit tells the kernel that all actions taken by the filter, with the 761// exception of ActAllow, should be logged. 762// The Log bit is only usable when libseccomp API level 3 or higher is 763// supported. 764func (f *ScmpFilter) GetLogBit() (bool, error) { 765 log, err := f.getFilterAttr(filterAttrLog) 766 if err != nil { 767 api, apiErr := getApi() 768 if (apiErr != nil && api == 0) || (apiErr == nil && api < 3) { 769 return false, fmt.Errorf("getting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher") 770 } 771 772 return false, err 773 } 774 775 if log == 0 { 776 return false, nil 777 } 778 779 return true, nil 780} 781 782// SetBadArchAction sets the default action taken on a syscall for an 783// architecture not in the filter, or an error if an issue was encountered 784// setting the value. 785func (f *ScmpFilter) SetBadArchAction(action ScmpAction) error { 786 if err := sanitizeAction(action); err != nil { 787 return err 788 } 789 790 return f.setFilterAttr(filterAttrActBadArch, action.toNative()) 791} 792 793// SetNoNewPrivsBit sets the state of the No New Privileges bit, which will be 794// applied on filter load, or an error if an issue was encountered setting the 795// value. 796// Filters with No New Privileges set to 0 can only be loaded if the process 797// has the CAP_SYS_ADMIN capability. 798func (f *ScmpFilter) SetNoNewPrivsBit(state bool) error { 799 var toSet C.uint32_t = 0x0 800 801 if state { 802 toSet = 0x1 803 } 804 805 return f.setFilterAttr(filterAttrNNP, toSet) 806} 807 808// SetLogBit sets the state of the Log bit, which will be applied on filter 809// load, or an error if an issue was encountered setting the value. 810// The Log bit is only usable when libseccomp API level 3 or higher is 811// supported. 812func (f *ScmpFilter) SetLogBit(state bool) error { 813 var toSet C.uint32_t = 0x0 814 815 if state { 816 toSet = 0x1 817 } 818 819 err := f.setFilterAttr(filterAttrLog, toSet) 820 if err != nil { 821 api, apiErr := getApi() 822 if (apiErr != nil && api == 0) || (apiErr == nil && api < 3) { 823 return fmt.Errorf("setting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher") 824 } 825 } 826 827 return err 828} 829 830// SetSyscallPriority sets a syscall's priority. 831// This provides a hint to the filter generator in libseccomp about the 832// importance of this syscall. High-priority syscalls are placed 833// first in the filter code, and incur less overhead (at the expense of 834// lower-priority syscalls). 835func (f *ScmpFilter) SetSyscallPriority(call ScmpSyscall, priority uint8) error { 836 f.lock.Lock() 837 defer f.lock.Unlock() 838 839 if !f.valid { 840 return errBadFilter 841 } 842 843 if retCode := C.seccomp_syscall_priority(f.filterCtx, C.int(call), 844 C.uint8_t(priority)); retCode != 0 { 845 return syscall.Errno(-1 * retCode) 846 } 847 848 return nil 849} 850 851// AddRule adds a single rule for an unconditional action on a syscall. 852// Accepts the number of the syscall and the action to be taken on the call 853// being made. 854// Returns an error if an issue was encountered adding the rule. 855func (f *ScmpFilter) AddRule(call ScmpSyscall, action ScmpAction) error { 856 return f.addRuleGeneric(call, action, false, nil) 857} 858 859// AddRuleExact adds a single rule for an unconditional action on a syscall. 860// Accepts the number of the syscall and the action to be taken on the call 861// being made. 862// No modifications will be made to the rule, and it will fail to add if it 863// cannot be applied to the current architecture without modification. 864// The rule will function exactly as described, but it may not function identically 865// (or be able to be applied to) all architectures. 866// Returns an error if an issue was encountered adding the rule. 867func (f *ScmpFilter) AddRuleExact(call ScmpSyscall, action ScmpAction) error { 868 return f.addRuleGeneric(call, action, true, nil) 869} 870 871// AddRuleConditional adds a single rule for a conditional action on a syscall. 872// Returns an error if an issue was encountered adding the rule. 873// All conditions must match for the rule to match. 874// There is a bug in library versions below v2.2.1 which can, in some cases, 875// cause conditions to be lost when more than one are used. Consequently, 876// AddRuleConditional is disabled on library versions lower than v2.2.1 877func (f *ScmpFilter) AddRuleConditional(call ScmpSyscall, action ScmpAction, conds []ScmpCondition) error { 878 return f.addRuleGeneric(call, action, false, conds) 879} 880 881// AddRuleConditionalExact adds a single rule for a conditional action on a 882// syscall. 883// No modifications will be made to the rule, and it will fail to add if it 884// cannot be applied to the current architecture without modification. 885// The rule will function exactly as described, but it may not function identically 886// (or be able to be applied to) all architectures. 887// Returns an error if an issue was encountered adding the rule. 888// There is a bug in library versions below v2.2.1 which can, in some cases, 889// cause conditions to be lost when more than one are used. Consequently, 890// AddRuleConditionalExact is disabled on library versions lower than v2.2.1 891func (f *ScmpFilter) AddRuleConditionalExact(call ScmpSyscall, action ScmpAction, conds []ScmpCondition) error { 892 return f.addRuleGeneric(call, action, true, conds) 893} 894 895// ExportPFC output PFC-formatted, human-readable dump of a filter context's 896// rules to a file. 897// Accepts file to write to (must be open for writing). 898// Returns an error if writing to the file fails. 899func (f *ScmpFilter) ExportPFC(file *os.File) error { 900 f.lock.Lock() 901 defer f.lock.Unlock() 902 903 fd := file.Fd() 904 905 if !f.valid { 906 return errBadFilter 907 } 908 909 if retCode := C.seccomp_export_pfc(f.filterCtx, C.int(fd)); retCode != 0 { 910 return syscall.Errno(-1 * retCode) 911 } 912 913 return nil 914} 915 916// ExportBPF outputs Berkeley Packet Filter-formatted, kernel-readable dump of a 917// filter context's rules to a file. 918// Accepts file to write to (must be open for writing). 919// Returns an error if writing to the file fails. 920func (f *ScmpFilter) ExportBPF(file *os.File) error { 921 f.lock.Lock() 922 defer f.lock.Unlock() 923 924 fd := file.Fd() 925 926 if !f.valid { 927 return errBadFilter 928 } 929 930 if retCode := C.seccomp_export_bpf(f.filterCtx, C.int(fd)); retCode != 0 { 931 return syscall.Errno(-1 * retCode) 932 } 933 934 return nil 935} 936