github.com/sdibtacm/sandbox@v0.0.0-20200320120712-60470cf803dc/units/seccomp/seccomp_internal.go (about)

     1  // +build linux
     2  
     3  // Internal functions for libseccomp Go bindings
     4  // No exported functions
     5  
     6  package seccomp
     7  
     8  import (
     9  	"fmt"
    10  	"syscall"
    11  )
    12  
    13  // Unexported C wrapping code - provides the C-Golang interface
    14  // Get the seccomp header in scope
    15  // Need stdlib.h for free() on cstrings
    16  
    17  // #cgo pkg-config: libseccomp
    18  /*
    19  #include <errno.h>
    20  #include <stdlib.h>
    21  #include <seccomp.h>
    22  
    23  #if SCMP_VER_MAJOR < 2
    24  #error Minimum supported version of Libseccomp is v2.2.0
    25  #elif SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 2
    26  #error Minimum supported version of Libseccomp is v2.2.0
    27  #endif
    28  
    29  #define ARCH_BAD ~0
    30  
    31  const uint32_t C_ARCH_BAD = ARCH_BAD;
    32  
    33  #ifndef SCMP_ARCH_PPC
    34  #define SCMP_ARCH_PPC ARCH_BAD
    35  #endif
    36  
    37  #ifndef SCMP_ARCH_PPC64
    38  #define SCMP_ARCH_PPC64 ARCH_BAD
    39  #endif
    40  
    41  #ifndef SCMP_ARCH_PPC64LE
    42  #define SCMP_ARCH_PPC64LE ARCH_BAD
    43  #endif
    44  
    45  #ifndef SCMP_ARCH_S390
    46  #define SCMP_ARCH_S390 ARCH_BAD
    47  #endif
    48  
    49  #ifndef SCMP_ARCH_S390X
    50  #define SCMP_ARCH_S390X ARCH_BAD
    51  #endif
    52  
    53  const uint32_t C_ARCH_NATIVE       = SCMP_ARCH_NATIVE;
    54  const uint32_t C_ARCH_X86          = SCMP_ARCH_X86;
    55  const uint32_t C_ARCH_X86_64       = SCMP_ARCH_X86_64;
    56  const uint32_t C_ARCH_X32          = SCMP_ARCH_X32;
    57  const uint32_t C_ARCH_ARM          = SCMP_ARCH_ARM;
    58  const uint32_t C_ARCH_AARCH64      = SCMP_ARCH_AARCH64;
    59  const uint32_t C_ARCH_MIPS         = SCMP_ARCH_MIPS;
    60  const uint32_t C_ARCH_MIPS64       = SCMP_ARCH_MIPS64;
    61  const uint32_t C_ARCH_MIPS64N32    = SCMP_ARCH_MIPS64N32;
    62  const uint32_t C_ARCH_MIPSEL       = SCMP_ARCH_MIPSEL;
    63  const uint32_t C_ARCH_MIPSEL64     = SCMP_ARCH_MIPSEL64;
    64  const uint32_t C_ARCH_MIPSEL64N32  = SCMP_ARCH_MIPSEL64N32;
    65  const uint32_t C_ARCH_PPC          = SCMP_ARCH_PPC;
    66  const uint32_t C_ARCH_PPC64        = SCMP_ARCH_PPC64;
    67  const uint32_t C_ARCH_PPC64LE      = SCMP_ARCH_PPC64LE;
    68  const uint32_t C_ARCH_S390         = SCMP_ARCH_S390;
    69  const uint32_t C_ARCH_S390X        = SCMP_ARCH_S390X;
    70  
    71  #ifndef SCMP_ACT_LOG
    72  #define SCMP_ACT_LOG 0x7ffc0000U
    73  #endif
    74  
    75  const uint32_t C_ACT_KILL          = SCMP_ACT_KILL;
    76  const uint32_t C_ACT_TRAP          = SCMP_ACT_TRAP;
    77  const uint32_t C_ACT_ERRNO         = SCMP_ACT_ERRNO(0);
    78  const uint32_t C_ACT_TRACE         = SCMP_ACT_TRACE(0);
    79  const uint32_t C_ACT_LOG           = SCMP_ACT_LOG;
    80  const uint32_t C_ACT_ALLOW         = SCMP_ACT_ALLOW;
    81  
    82  // The libseccomp SCMP_FLTATR_CTL_LOG member of the scmp_filter_attr enum was
    83  // added in v2.4.0
    84  #if (SCMP_VER_MAJOR < 2) || \
    85      (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 4)
    86  #define SCMP_FLTATR_CTL_LOG _SCMP_FLTATR_MIN
    87  #endif
    88  
    89  const uint32_t C_ATTRIBUTE_DEFAULT = (uint32_t)SCMP_FLTATR_ACT_DEFAULT;
    90  const uint32_t C_ATTRIBUTE_BADARCH = (uint32_t)SCMP_FLTATR_ACT_BADARCH;
    91  const uint32_t C_ATTRIBUTE_NNP     = (uint32_t)SCMP_FLTATR_CTL_NNP;
    92  const uint32_t C_ATTRIBUTE_TSYNC   = (uint32_t)SCMP_FLTATR_CTL_TSYNC;
    93  const uint32_t C_ATTRIBUTE_LOG     = (uint32_t)SCMP_FLTATR_CTL_LOG;
    94  
    95  const int      C_CMP_NE            = (int)SCMP_CMP_NE;
    96  const int      C_CMP_LT            = (int)SCMP_CMP_LT;
    97  const int      C_CMP_LE            = (int)SCMP_CMP_LE;
    98  const int      C_CMP_EQ            = (int)SCMP_CMP_EQ;
    99  const int      C_CMP_GE            = (int)SCMP_CMP_GE;
   100  const int      C_CMP_GT            = (int)SCMP_CMP_GT;
   101  const int      C_CMP_MASKED_EQ     = (int)SCMP_CMP_MASKED_EQ;
   102  
   103  const int      C_VERSION_MAJOR     = SCMP_VER_MAJOR;
   104  const int      C_VERSION_MINOR     = SCMP_VER_MINOR;
   105  const int      C_VERSION_MICRO     = SCMP_VER_MICRO;
   106  
   107  #if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR >= 3
   108  unsigned int get_major_version()
   109  {
   110          return seccomp_version()->major;
   111  }
   112  
   113  unsigned int get_minor_version()
   114  {
   115          return seccomp_version()->minor;
   116  }
   117  
   118  unsigned int get_micro_version()
   119  {
   120          return seccomp_version()->micro;
   121  }
   122  #else
   123  unsigned int get_major_version()
   124  {
   125          return (unsigned int)C_VERSION_MAJOR;
   126  }
   127  
   128  unsigned int get_minor_version()
   129  {
   130          return (unsigned int)C_VERSION_MINOR;
   131  }
   132  
   133  unsigned int get_micro_version()
   134  {
   135          return (unsigned int)C_VERSION_MICRO;
   136  }
   137  #endif
   138  
   139  // The libseccomp API level functions were added in v2.4.0
   140  #if (SCMP_VER_MAJOR < 2) || \
   141      (SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 4)
   142  const unsigned int seccomp_api_get(void)
   143  {
   144  	// libseccomp-golang requires libseccomp v2.2.0, at a minimum, which
   145  	// supported API level 2. However, the kernel may not support API level
   146  	// 2 constructs which are the seccomp() system call and the TSYNC
   147  	// filter flag. Return the "reserved" value of 0 here to indicate that
   148  	// proper API level support is not available in libseccomp.
   149  	return 0;
   150  }
   151  
   152  int seccomp_api_set(unsigned int level)
   153  {
   154  	return -EOPNOTSUPP;
   155  }
   156  #endif
   157  
   158  typedef struct scmp_arg_cmp* scmp_cast_t;
   159  
   160  void* make_arg_cmp_array(unsigned int length)
   161  {
   162          return calloc(length, sizeof(struct scmp_arg_cmp));
   163  }
   164  
   165  // Wrapper to add an scmp_arg_cmp struct to an existing arg_cmp array
   166  void add_struct_arg_cmp(
   167                          struct scmp_arg_cmp* arr,
   168                          unsigned int pos,
   169                          unsigned int arg,
   170                          int compare,
   171                          uint64_t a,
   172                          uint64_t b
   173                         )
   174  {
   175          arr[pos].arg = arg;
   176          arr[pos].op = compare;
   177          arr[pos].datum_a = a;
   178          arr[pos].datum_b = b;
   179  
   180          return;
   181  }
   182  */
   183  import "C"
   184  
   185  // Nonexported types
   186  type scmpFilterAttr uint32
   187  
   188  // Nonexported constants
   189  
   190  const (
   191  	filterAttrActDefault scmpFilterAttr = iota
   192  	filterAttrActBadArch scmpFilterAttr = iota
   193  	filterAttrNNP        scmpFilterAttr = iota
   194  	filterAttrTsync      scmpFilterAttr = iota
   195  	filterAttrLog        scmpFilterAttr = iota
   196  )
   197  
   198  const (
   199  	// An error return from certain libseccomp functions
   200  	scmpError C.int = -1
   201  	// Comparison boundaries to check for architecture validity
   202  	archStart ScmpArch = ArchNative
   203  	archEnd   ScmpArch = ArchS390X
   204  	// Comparison boundaries to check for action validity
   205  	actionStart ScmpAction = ActKill
   206  	actionEnd   ScmpAction = ActLog
   207  	// Comparison boundaries to check for comparison operator validity
   208  	compareOpStart ScmpCompareOp = CompareNotEqual
   209  	compareOpEnd   ScmpCompareOp = CompareMaskedEqual
   210  )
   211  
   212  var (
   213  	// Error thrown on bad filter context
   214  	errBadFilter = fmt.Errorf("filter is invalid or uninitialized")
   215  	// Constants representing library major, minor, and micro versions
   216  	verMajor = uint(C.get_major_version())
   217  	verMinor = uint(C.get_minor_version())
   218  	verMicro = uint(C.get_micro_version())
   219  )
   220  
   221  // Nonexported functions
   222  
   223  // Check if library version is greater than or equal to the given one
   224  func checkVersionAbove(major, minor, micro uint) bool {
   225  	return (verMajor > major) ||
   226  		(verMajor == major && verMinor > minor) ||
   227  		(verMajor == major && verMinor == minor && verMicro >= micro)
   228  }
   229  
   230  // Ensure that the library is supported, i.e. >= 2.2.0.
   231  func ensureSupportedVersion() error {
   232  	if !checkVersionAbove(2, 2, 0) {
   233  		return VersionError{}
   234  	}
   235  	return nil
   236  }
   237  
   238  // Get the API level
   239  func getApi() (uint, error) {
   240  	api := C.seccomp_api_get()
   241  	if api == 0 {
   242  		return 0, fmt.Errorf("API level operations are not supported")
   243  	}
   244  
   245  	return uint(api), nil
   246  }
   247  
   248  // Set the API level
   249  func setApi(api uint) error {
   250  	if retCode := C.seccomp_api_set(C.uint(api)); retCode != 0 {
   251  		if syscall.Errno(-1*retCode) == syscall.EOPNOTSUPP {
   252  			return fmt.Errorf("API level operations are not supported")
   253  		}
   254  
   255  		return fmt.Errorf("could not set API level: %v", retCode)
   256  	}
   257  
   258  	return nil
   259  }
   260  
   261  // Filter helpers
   262  
   263  // Filter finalizer - ensure that kernel context for filters is freed
   264  func filterFinalizer(f *ScmpFilter) {
   265  	f.Release()
   266  }
   267  
   268  // Get a raw filter attribute
   269  func (f *ScmpFilter) getFilterAttr(attr scmpFilterAttr) (C.uint32_t, error) {
   270  	f.lock.Lock()
   271  	defer f.lock.Unlock()
   272  
   273  	if !f.valid {
   274  		return 0x0, errBadFilter
   275  	}
   276  
   277  	var attribute C.uint32_t
   278  
   279  	retCode := C.seccomp_attr_get(f.filterCtx, attr.toNative(), &attribute)
   280  	if retCode != 0 {
   281  		return 0x0, syscall.Errno(-1 * retCode)
   282  	}
   283  
   284  	return attribute, nil
   285  }
   286  
   287  // Set a raw filter attribute
   288  func (f *ScmpFilter) setFilterAttr(attr scmpFilterAttr, value C.uint32_t) error {
   289  	f.lock.Lock()
   290  	defer f.lock.Unlock()
   291  
   292  	if !f.valid {
   293  		return errBadFilter
   294  	}
   295  
   296  	retCode := C.seccomp_attr_set(f.filterCtx, attr.toNative(), value)
   297  	if retCode != 0 {
   298  		return syscall.Errno(-1 * retCode)
   299  	}
   300  
   301  	return nil
   302  }
   303  
   304  // DOES NOT LOCK OR CHECK VALIDITY
   305  // Assumes caller has already done this
   306  // Wrapper for seccomp_rule_add_... functions
   307  func (f *ScmpFilter) addRuleWrapper(call ScmpSyscall, action ScmpAction, exact bool, length C.uint, cond C.scmp_cast_t) error {
   308  	if length != 0 && cond == nil {
   309  		return fmt.Errorf("null conditions list, but length is nonzero")
   310  	}
   311  
   312  	var retCode C.int
   313  	if exact {
   314  		retCode = C.seccomp_rule_add_exact_array(f.filterCtx, action.toNative(), C.int(call), length, cond)
   315  	} else {
   316  		retCode = C.seccomp_rule_add_array(f.filterCtx, action.toNative(), C.int(call), length, cond)
   317  	}
   318  
   319  	if syscall.Errno(-1*retCode) == syscall.EFAULT {
   320  		return fmt.Errorf("unrecognized syscall %#x", int32(call))
   321  	} else if syscall.Errno(-1*retCode) == syscall.EPERM {
   322  		return fmt.Errorf("requested action matches default action of filter")
   323  	} else if syscall.Errno(-1*retCode) == syscall.EINVAL {
   324  		return fmt.Errorf("two checks on same syscall argument")
   325  	} else if retCode != 0 {
   326  		return syscall.Errno(-1 * retCode)
   327  	}
   328  
   329  	return nil
   330  }
   331  
   332  // Generic add function for filter rules
   333  func (f *ScmpFilter) addRuleGeneric(call ScmpSyscall, action ScmpAction, exact bool, conds []ScmpCondition) error {
   334  	f.lock.Lock()
   335  	defer f.lock.Unlock()
   336  
   337  	if !f.valid {
   338  		return errBadFilter
   339  	}
   340  
   341  	if len(conds) == 0 {
   342  		if err := f.addRuleWrapper(call, action, exact, 0, nil); err != nil {
   343  			return err
   344  		}
   345  	} else {
   346  		// We don't support conditional filtering in library version v2.1
   347  		if !checkVersionAbove(2, 2, 1) {
   348  			return VersionError{
   349  				message: "conditional filtering is not supported",
   350  				minimum: "2.2.1",
   351  			}
   352  		}
   353  
   354  		argsArr := C.make_arg_cmp_array(C.uint(len(conds)))
   355  		if argsArr == nil {
   356  			return fmt.Errorf("error allocating memory for conditions")
   357  		}
   358  		defer C.free(argsArr)
   359  
   360  		for i, cond := range conds {
   361  			C.add_struct_arg_cmp(C.scmp_cast_t(argsArr), C.uint(i),
   362  				C.uint(cond.Argument), cond.Op.toNative(),
   363  				C.uint64_t(cond.Operand1), C.uint64_t(cond.Operand2))
   364  		}
   365  
   366  		if err := f.addRuleWrapper(call, action, exact, C.uint(len(conds)), C.scmp_cast_t(argsArr)); err != nil {
   367  			return err
   368  		}
   369  	}
   370  
   371  	return nil
   372  }
   373  
   374  // Generic Helpers
   375  
   376  // Helper - Sanitize Arch token input
   377  func sanitizeArch(in ScmpArch) error {
   378  	if in < archStart || in > archEnd {
   379  		return fmt.Errorf("unrecognized architecture %#x", uint(in))
   380  	}
   381  
   382  	if in.toNative() == C.C_ARCH_BAD {
   383  		return fmt.Errorf("architecture %v is not supported on this version of the library", in)
   384  	}
   385  
   386  	return nil
   387  }
   388  
   389  func sanitizeAction(in ScmpAction) error {
   390  	inTmp := in & 0x0000FFFF
   391  	if inTmp < actionStart || inTmp > actionEnd {
   392  		return fmt.Errorf("unrecognized action %#x", uint(inTmp))
   393  	}
   394  
   395  	if inTmp != ActTrace && inTmp != ActErrno && (in&0xFFFF0000) != 0 {
   396  		return fmt.Errorf("highest 16 bits must be zeroed except for Trace and Errno")
   397  	}
   398  
   399  	return nil
   400  }
   401  
   402  func sanitizeCompareOp(in ScmpCompareOp) error {
   403  	if in < compareOpStart || in > compareOpEnd {
   404  		return fmt.Errorf("unrecognized comparison operator %#x", uint(in))
   405  	}
   406  
   407  	return nil
   408  }
   409  
   410  func archFromNative(a C.uint32_t) (ScmpArch, error) {
   411  	switch a {
   412  	case C.C_ARCH_X86:
   413  		return ArchX86, nil
   414  	case C.C_ARCH_X86_64:
   415  		return ArchAMD64, nil
   416  	case C.C_ARCH_X32:
   417  		return ArchX32, nil
   418  	case C.C_ARCH_ARM:
   419  		return ArchARM, nil
   420  	case C.C_ARCH_NATIVE:
   421  		return ArchNative, nil
   422  	case C.C_ARCH_AARCH64:
   423  		return ArchARM64, nil
   424  	case C.C_ARCH_MIPS:
   425  		return ArchMIPS, nil
   426  	case C.C_ARCH_MIPS64:
   427  		return ArchMIPS64, nil
   428  	case C.C_ARCH_MIPS64N32:
   429  		return ArchMIPS64N32, nil
   430  	case C.C_ARCH_MIPSEL:
   431  		return ArchMIPSEL, nil
   432  	case C.C_ARCH_MIPSEL64:
   433  		return ArchMIPSEL64, nil
   434  	case C.C_ARCH_MIPSEL64N32:
   435  		return ArchMIPSEL64N32, nil
   436  	case C.C_ARCH_PPC:
   437  		return ArchPPC, nil
   438  	case C.C_ARCH_PPC64:
   439  		return ArchPPC64, nil
   440  	case C.C_ARCH_PPC64LE:
   441  		return ArchPPC64LE, nil
   442  	case C.C_ARCH_S390:
   443  		return ArchS390, nil
   444  	case C.C_ARCH_S390X:
   445  		return ArchS390X, nil
   446  	default:
   447  		return 0x0, fmt.Errorf("unrecognized architecture %#x", uint32(a))
   448  	}
   449  }
   450  
   451  // Only use with sanitized arches, no error handling
   452  func (a ScmpArch) toNative() C.uint32_t {
   453  	switch a {
   454  	case ArchX86:
   455  		return C.C_ARCH_X86
   456  	case ArchAMD64:
   457  		return C.C_ARCH_X86_64
   458  	case ArchX32:
   459  		return C.C_ARCH_X32
   460  	case ArchARM:
   461  		return C.C_ARCH_ARM
   462  	case ArchARM64:
   463  		return C.C_ARCH_AARCH64
   464  	case ArchMIPS:
   465  		return C.C_ARCH_MIPS
   466  	case ArchMIPS64:
   467  		return C.C_ARCH_MIPS64
   468  	case ArchMIPS64N32:
   469  		return C.C_ARCH_MIPS64N32
   470  	case ArchMIPSEL:
   471  		return C.C_ARCH_MIPSEL
   472  	case ArchMIPSEL64:
   473  		return C.C_ARCH_MIPSEL64
   474  	case ArchMIPSEL64N32:
   475  		return C.C_ARCH_MIPSEL64N32
   476  	case ArchPPC:
   477  		return C.C_ARCH_PPC
   478  	case ArchPPC64:
   479  		return C.C_ARCH_PPC64
   480  	case ArchPPC64LE:
   481  		return C.C_ARCH_PPC64LE
   482  	case ArchS390:
   483  		return C.C_ARCH_S390
   484  	case ArchS390X:
   485  		return C.C_ARCH_S390X
   486  	case ArchNative:
   487  		return C.C_ARCH_NATIVE
   488  	default:
   489  		return 0x0
   490  	}
   491  }
   492  
   493  // Only use with sanitized ops, no error handling
   494  func (a ScmpCompareOp) toNative() C.int {
   495  	switch a {
   496  	case CompareNotEqual:
   497  		return C.C_CMP_NE
   498  	case CompareLess:
   499  		return C.C_CMP_LT
   500  	case CompareLessOrEqual:
   501  		return C.C_CMP_LE
   502  	case CompareEqual:
   503  		return C.C_CMP_EQ
   504  	case CompareGreaterEqual:
   505  		return C.C_CMP_GE
   506  	case CompareGreater:
   507  		return C.C_CMP_GT
   508  	case CompareMaskedEqual:
   509  		return C.C_CMP_MASKED_EQ
   510  	default:
   511  		return 0x0
   512  	}
   513  }
   514  
   515  func actionFromNative(a C.uint32_t) (ScmpAction, error) {
   516  	aTmp := a & 0xFFFF
   517  	switch a & 0xFFFF0000 {
   518  	case C.C_ACT_KILL:
   519  		return ActKill, nil
   520  	case C.C_ACT_TRAP:
   521  		return ActTrap, nil
   522  	case C.C_ACT_ERRNO:
   523  		return ActErrno.SetReturnCode(int16(aTmp)), nil
   524  	case C.C_ACT_TRACE:
   525  		return ActTrace.SetReturnCode(int16(aTmp)), nil
   526  	case C.C_ACT_LOG:
   527  		return ActLog, nil
   528  	case C.C_ACT_ALLOW:
   529  		return ActAllow, nil
   530  	default:
   531  		return 0x0, fmt.Errorf("unrecognized action %#x", uint32(a))
   532  	}
   533  }
   534  
   535  // Only use with sanitized actions, no error handling
   536  func (a ScmpAction) toNative() C.uint32_t {
   537  	switch a & 0xFFFF {
   538  	case ActKill:
   539  		return C.C_ACT_KILL
   540  	case ActTrap:
   541  		return C.C_ACT_TRAP
   542  	case ActErrno:
   543  		return C.C_ACT_ERRNO | (C.uint32_t(a) >> 16)
   544  	case ActTrace:
   545  		return C.C_ACT_TRACE | (C.uint32_t(a) >> 16)
   546  	case ActLog:
   547  		return C.C_ACT_LOG
   548  	case ActAllow:
   549  		return C.C_ACT_ALLOW
   550  	default:
   551  		return 0x0
   552  	}
   553  }
   554  
   555  // Internal only, assumes safe attribute
   556  func (a scmpFilterAttr) toNative() uint32 {
   557  	switch a {
   558  	case filterAttrActDefault:
   559  		return uint32(C.C_ATTRIBUTE_DEFAULT)
   560  	case filterAttrActBadArch:
   561  		return uint32(C.C_ATTRIBUTE_BADARCH)
   562  	case filterAttrNNP:
   563  		return uint32(C.C_ATTRIBUTE_NNP)
   564  	case filterAttrTsync:
   565  		return uint32(C.C_ATTRIBUTE_TSYNC)
   566  	case filterAttrLog:
   567  		return uint32(C.C_ATTRIBUTE_LOG)
   568  	default:
   569  		return 0x0
   570  	}
   571  }