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 }