github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/security/seccomp/seccomp_linux.go (about)

     1  // Copyright (c) 2018, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  // +build seccomp
     7  
     8  package seccomp
     9  
    10  import (
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"syscall"
    15  
    16  	"github.com/opencontainers/runtime-tools/generate"
    17  
    18  	"github.com/sylabs/singularity/internal/pkg/sylog"
    19  
    20  	cseccomp "github.com/kubernetes-sigs/cri-o/pkg/seccomp"
    21  	specs "github.com/opencontainers/runtime-spec/specs-go"
    22  	lseccomp "github.com/seccomp/libseccomp-golang"
    23  )
    24  
    25  var scmpArchMap = map[specs.Arch]lseccomp.ScmpArch{
    26  	"":                    lseccomp.ArchNative,
    27  	specs.ArchX86:         lseccomp.ArchX86,
    28  	specs.ArchX86_64:      lseccomp.ArchAMD64,
    29  	specs.ArchX32:         lseccomp.ArchX32,
    30  	specs.ArchARM:         lseccomp.ArchARM,
    31  	specs.ArchAARCH64:     lseccomp.ArchARM64,
    32  	specs.ArchMIPS:        lseccomp.ArchMIPS,
    33  	specs.ArchMIPS64:      lseccomp.ArchMIPS64,
    34  	specs.ArchMIPS64N32:   lseccomp.ArchMIPS64N32,
    35  	specs.ArchMIPSEL:      lseccomp.ArchMIPSEL,
    36  	specs.ArchMIPSEL64:    lseccomp.ArchMIPSEL64,
    37  	specs.ArchMIPSEL64N32: lseccomp.ArchMIPSEL64N32,
    38  	specs.ArchPPC:         lseccomp.ArchPPC,
    39  	specs.ArchPPC64:       lseccomp.ArchPPC64,
    40  	specs.ArchPPC64LE:     lseccomp.ArchPPC64LE,
    41  	specs.ArchS390:        lseccomp.ArchS390,
    42  	specs.ArchS390X:       lseccomp.ArchS390X,
    43  }
    44  
    45  var scmpActionMap = map[specs.LinuxSeccompAction]lseccomp.ScmpAction{
    46  	specs.ActKill:  lseccomp.ActKill,
    47  	specs.ActTrap:  lseccomp.ActTrap,
    48  	specs.ActErrno: lseccomp.ActErrno,
    49  	specs.ActTrace: lseccomp.ActTrace,
    50  	specs.ActAllow: lseccomp.ActAllow,
    51  }
    52  
    53  var scmpCompareOpMap = map[specs.LinuxSeccompOperator]lseccomp.ScmpCompareOp{
    54  	specs.OpNotEqual:     lseccomp.CompareNotEqual,
    55  	specs.OpLessThan:     lseccomp.CompareLess,
    56  	specs.OpLessEqual:    lseccomp.CompareLessOrEqual,
    57  	specs.OpEqualTo:      lseccomp.CompareEqual,
    58  	specs.OpGreaterEqual: lseccomp.CompareGreaterEqual,
    59  	specs.OpGreaterThan:  lseccomp.CompareGreater,
    60  	specs.OpMaskedEqual:  lseccomp.CompareMaskedEqual,
    61  }
    62  
    63  func prctl(option uintptr, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) syscall.Errno {
    64  	_, _, err := syscall.Syscall6(syscall.SYS_PRCTL, option, arg2, arg3, arg4, arg5, 0)
    65  	return err
    66  }
    67  
    68  func hasConditionSupport() bool {
    69  	major, minor, micro := lseccomp.GetLibraryVersion()
    70  	return (major > 2) || (major == 2 && minor >= 2) || (major == 2 && minor == 2 && micro >= 1)
    71  }
    72  
    73  // Enabled returns wether seccomp is enabled or not
    74  func Enabled() bool {
    75  	return true
    76  }
    77  
    78  // LoadSeccompConfig loads seccomp configuration filter for the current process
    79  func LoadSeccompConfig(config *specs.LinuxSeccomp, noNewPrivs bool) error {
    80  	supportCondition := true
    81  
    82  	if err := prctl(syscall.PR_GET_SECCOMP, 0, 0, 0, 0); err == syscall.EINVAL {
    83  		return fmt.Errorf("can't load seccomp filter: not supported by kernel")
    84  	}
    85  
    86  	if err := prctl(syscall.PR_SET_SECCOMP, 2, 0, 0, 0); err == syscall.EINVAL {
    87  		return fmt.Errorf("can't load seccomp filter: SECCOMP_MODE_FILTER not supported")
    88  	}
    89  
    90  	if config == nil {
    91  		return fmt.Errorf("empty config passed")
    92  	}
    93  
    94  	if len(config.DefaultAction) == 0 {
    95  		return fmt.Errorf("a defaultAction must be provided")
    96  	}
    97  
    98  	supportCondition = hasConditionSupport()
    99  	if supportCondition == false {
   100  		sylog.Warningf("seccomp rule conditions are not supported with libseccomp under 2.2.1")
   101  	}
   102  
   103  	scmpAction, ok := scmpActionMap[config.DefaultAction]
   104  	if !ok {
   105  		return fmt.Errorf("invalid action '%s' specified", config.DefaultAction)
   106  	}
   107  	if scmpAction == lseccomp.ActErrno {
   108  		scmpAction = scmpAction.SetReturnCode(1)
   109  	}
   110  
   111  	filter, err := lseccomp.NewFilter(scmpAction)
   112  	if err != nil {
   113  		return fmt.Errorf("error creating new filter: %s", err)
   114  	}
   115  
   116  	if err := filter.SetNoNewPrivsBit(noNewPrivs); err != nil {
   117  		return fmt.Errorf("failed to set no new priv flag: %s", err)
   118  	}
   119  
   120  	for _, arch := range config.Architectures {
   121  		scmpArch, ok := scmpArchMap[arch]
   122  		if !ok {
   123  			return fmt.Errorf("invalid architecture '%s' specified", arch)
   124  		}
   125  
   126  		if err := filter.AddArch(scmpArch); err != nil {
   127  			return fmt.Errorf("error adding architecture: %s", err)
   128  		}
   129  	}
   130  
   131  	for _, syscall := range config.Syscalls {
   132  		if len(syscall.Names) == 0 {
   133  			return fmt.Errorf("no syscall specified for the rule")
   134  		}
   135  
   136  		scmpAction, ok = scmpActionMap[syscall.Action]
   137  		if !ok {
   138  			return fmt.Errorf("invalid action '%s' specified", syscall.Action)
   139  		}
   140  		if scmpAction == lseccomp.ActErrno {
   141  			scmpAction = scmpAction.SetReturnCode(1)
   142  		}
   143  
   144  		for _, sysName := range syscall.Names {
   145  			sysNr, err := lseccomp.GetSyscallFromName(sysName)
   146  			if err != nil {
   147  				continue
   148  			}
   149  
   150  			if len(syscall.Args) == 0 || supportCondition == false {
   151  				if err := filter.AddRule(sysNr, scmpAction); err != nil {
   152  					return fmt.Errorf("failed adding seccomp rule for syscall %s: %s", sysName, err)
   153  				}
   154  			} else {
   155  				conditions, err := addSyscallRuleContitions(syscall.Args)
   156  				if err != nil {
   157  					return err
   158  				}
   159  				if err := filter.AddRuleConditional(sysNr, scmpAction, conditions); err != nil {
   160  					return fmt.Errorf("failed adding rule condition for syscall %s: %s", sysName, err)
   161  				}
   162  			}
   163  		}
   164  	}
   165  
   166  	if err = filter.Load(); err != nil {
   167  		return fmt.Errorf("failed loading seccomp filter: %s", err)
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  func addSyscallRuleContitions(args []specs.LinuxSeccompArg) ([]lseccomp.ScmpCondition, error) {
   174  	var maxIndex uint = 6
   175  	conditions := make([]lseccomp.ScmpCondition, 0)
   176  
   177  	for _, arg := range args {
   178  		if arg.Index >= maxIndex {
   179  			return conditions, fmt.Errorf("the maximum index of syscall arguments is %d: given %d", maxIndex, arg.Index)
   180  		}
   181  		operator, ok := scmpCompareOpMap[arg.Op]
   182  		if !ok {
   183  			return conditions, fmt.Errorf("invalid operator encountered %s", arg.Op)
   184  		}
   185  		cond, err := lseccomp.MakeCondition(arg.Index, operator, arg.Value, arg.ValueTwo)
   186  		if err != nil {
   187  			return conditions, fmt.Errorf("error making syscall rule condition: %s", err)
   188  		}
   189  		conditions = append(conditions, cond)
   190  	}
   191  
   192  	return conditions, nil
   193  }
   194  
   195  // LoadProfileFromFile loads seccomp rules from json file and fill in
   196  // provided OCI configuration
   197  func LoadProfileFromFile(profile string, generator *generate.Generator) error {
   198  	file, err := os.Open(profile)
   199  	if err != nil {
   200  		return err
   201  	}
   202  	defer file.Close()
   203  
   204  	data, err := ioutil.ReadAll(file)
   205  	if err != nil {
   206  		return err
   207  	}
   208  
   209  	if generator.Config.Linux == nil {
   210  		generator.Config.Linux = &specs.Linux{}
   211  	}
   212  	if generator.Config.Linux.Seccomp == nil {
   213  		generator.Config.Linux.Seccomp = &specs.LinuxSeccomp{}
   214  	}
   215  	if generator.Config.Process == nil {
   216  		generator.Config.Process = &specs.Process{}
   217  	}
   218  	if generator.Config.Process.Capabilities == nil {
   219  		generator.Config.Process.Capabilities = &specs.LinuxCapabilities{}
   220  	}
   221  	if err := cseccomp.LoadProfileFromBytes(data, generator); err != nil {
   222  		return err
   223  	}
   224  	return nil
   225  }