github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/snap-seccomp/main_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "math/rand" 26 "os" 27 "os/exec" 28 "path/filepath" 29 "strconv" 30 "strings" 31 "testing" 32 33 . "gopkg.in/check.v1" 34 35 "github.com/mvo5/libseccomp-golang" 36 37 "github.com/snapcore/snapd/arch" 38 main "github.com/snapcore/snapd/cmd/snap-seccomp" 39 "github.com/snapcore/snapd/osutil" 40 "github.com/snapcore/snapd/release" 41 "github.com/snapcore/snapd/testutil" 42 ) 43 44 // Hook up check.v1 into the "go test" runner 45 func Test(t *testing.T) { TestingT(t) } 46 47 type snapSeccompSuite struct { 48 seccompBpfLoader string 49 seccompSyscallRunner string 50 canCheckCompatArch bool 51 } 52 53 var _ = Suite(&snapSeccompSuite{}) 54 55 const ( 56 Deny = iota 57 Allow 58 ) 59 60 var seccompBpfLoaderContent = []byte(` 61 #include <fcntl.h> 62 #include <inttypes.h> 63 #include <stdint.h> 64 #include <stdio.h> 65 #include <stdlib.h> 66 #include <string.h> 67 #include <sys/prctl.h> 68 #include <unistd.h> 69 70 #include <linux/filter.h> 71 #include <linux/seccomp.h> 72 73 #define MAX_BPF_SIZE 32 * 1024 74 75 int sc_apply_seccomp_bpf(const char* profile_path) 76 { 77 unsigned char bpf[MAX_BPF_SIZE + 1]; // account for EOF 78 FILE* fp; 79 fp = fopen(profile_path, "rb"); 80 if (fp == NULL) { 81 fprintf(stderr, "cannot read %s\n", profile_path); 82 return -1; 83 } 84 85 // set 'size' to 1; to get bytes transferred 86 size_t num_read = fread(bpf, 1, sizeof(bpf), fp); 87 88 if (ferror(fp) != 0) { 89 perror("fread()"); 90 return -1; 91 } else if (feof(fp) == 0) { 92 fprintf(stderr, "file too big\n"); 93 return -1; 94 } 95 fclose(fp); 96 97 struct sock_fprog prog = { 98 .len = num_read / sizeof(struct sock_filter), 99 .filter = (struct sock_filter*)bpf, 100 }; 101 102 // Set NNP to allow loading seccomp policy into the kernel without 103 // root 104 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { 105 perror("prctl(PR_NO_NEW_PRIVS, 1, 0, 0, 0)"); 106 return -1; 107 } 108 109 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { 110 perror("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, ...) failed"); 111 return -1; 112 } 113 return 0; 114 } 115 116 int main(int argc, char* argv[]) 117 { 118 int rc = 0; 119 if (argc < 2) { 120 fprintf(stderr, "Usage: %s <bpf file> [prog ...]\n", argv[0]); 121 return 1; 122 } 123 124 rc = sc_apply_seccomp_bpf(argv[1]); 125 if (rc != 0) 126 return -rc; 127 128 execv(argv[2], (char* const*)&argv[2]); 129 perror("execv failed"); 130 return 1; 131 } 132 `) 133 134 var seccompSyscallRunnerContent = []byte(` 135 #define _GNU_SOURCE 136 #include <errno.h> 137 #include <stdlib.h> 138 #include <sys/syscall.h> 139 #include <unistd.h> 140 #include <inttypes.h> 141 int main(int argc, char** argv) 142 { 143 uint32_t l[7]; 144 int syscall_ret, ret = 0; 145 for (int i = 0; i < 7 && argv[i+1] != NULL; i++) { 146 errno = 0; 147 l[i] = strtoll(argv[i + 1], NULL, 10); 148 // exit '11' let's us know strtoll failed 149 if (errno != 0) 150 syscall(SYS_exit, 11, 0, 0, 0, 0, 0); 151 } 152 // There might be architecture-specific requirements. see "man syscall" 153 // for details. 154 syscall_ret = syscall(l[0], l[1], l[2], l[3], l[4], l[5], l[6]); 155 // 911 is our mocked errno 156 if (syscall_ret < 0 && errno == 911) { 157 ret = 10; 158 } 159 syscall(SYS_exit, ret, 0, 0, 0, 0, 0); 160 return 0; 161 } 162 `) 163 164 func (s *snapSeccompSuite) SetUpSuite(c *C) { 165 main.MockErrnoOnDenial(911) 166 167 // build seccomp-load helper 168 s.seccompBpfLoader = filepath.Join(c.MkDir(), "seccomp_bpf_loader") 169 err := ioutil.WriteFile(s.seccompBpfLoader+".c", seccompBpfLoaderContent, 0644) 170 c.Assert(err, IsNil) 171 cmd := exec.Command("gcc", "-Werror", "-Wall", s.seccompBpfLoader+".c", "-o", s.seccompBpfLoader) 172 cmd.Stdout = os.Stdout 173 cmd.Stderr = os.Stderr 174 err = cmd.Run() 175 c.Assert(err, IsNil) 176 177 // build syscall-runner helper 178 s.seccompSyscallRunner = filepath.Join(c.MkDir(), "seccomp_syscall_runner") 179 err = ioutil.WriteFile(s.seccompSyscallRunner+".c", seccompSyscallRunnerContent, 0644) 180 c.Assert(err, IsNil) 181 182 cmd = exec.Command("gcc", "-std=c99", "-Werror", "-Wall", "-static", s.seccompSyscallRunner+".c", "-o", s.seccompSyscallRunner, "-Wl,-static", "-static-libgcc") 183 cmd.Stdout = os.Stdout 184 cmd.Stderr = os.Stderr 185 err = cmd.Run() 186 c.Assert(err, IsNil) 187 188 // Amazon Linux 2 is 64bit only and there is no multilib support 189 s.canCheckCompatArch = !release.DistroLike("amzn") 190 191 // Build 32bit runner on amd64 to test non-native syscall handling. 192 // Ideally we would build for ppc64el->powerpc and arm64->armhf but 193 // it seems tricky to find the right gcc-multilib for this. 194 if arch.DpkgArchitecture() == "amd64" && s.canCheckCompatArch { 195 cmd = exec.Command(cmd.Args[0], cmd.Args[1:]...) 196 cmd.Args = append(cmd.Args, "-m32") 197 for i, k := range cmd.Args { 198 if k == s.seccompSyscallRunner { 199 cmd.Args[i] = s.seccompSyscallRunner + ".m32" 200 } 201 } 202 if output, err := cmd.CombinedOutput(); err != nil { 203 fmt.Printf("cannot build multi-lib syscall runner: %v\n%s", err, output) 204 } 205 } 206 } 207 208 // Runs the policy through the kernel: 209 // 1. runs main.Compile() 210 // 2. the program in seccompBpfLoaderContent with the output file as an 211 // argument 212 // 3. the program in seccompBpfLoaderContent loads the output file BPF into 213 // the kernel and executes the program in seccompBpfRunnerContent with the 214 // syscall and arguments specified by the test 215 // 216 // In this manner, in addition to verifying policy syntax we are able to 217 // unit test the resulting bpf in several ways. 218 // 219 // Full testing of applied policy is done elsewhere via spread tests. 220 // 221 // Note that we skip testing prctl(PR_SET_ENDIAN) - it causes havoc when 222 // it is run. We will also need to skip: fadvise64_64, 223 // ftruncate64, posix_fadvise, pread64, pwrite64, readahead, 224 // sync_file_range, and truncate64. 225 // Once we start using those. See `man syscall` 226 func (s *snapSeccompSuite) runBpf(c *C, seccompWhitelist, bpfInput string, expected int) { 227 // Common syscalls we need to allow for a minimal statically linked 228 // c program. 229 // 230 // If we compile a test program for each test we can get away with 231 // a even smaller set of syscalls: execve,exit essentially. But it 232 // means a much longer test run (30s vs 2s). Commit d288d89 contains 233 // the code for this. 234 common := ` 235 execve 236 uname 237 brk 238 arch_prctl 239 readlink 240 access 241 sysinfo 242 exit 243 # i386 244 set_thread_area 245 # armhf 246 set_tls 247 # arm64 248 readlinkat 249 faccessat 250 # i386 from amd64 251 restart_syscall 252 # libc6 2.31/gcc-9.3 253 mprotect 254 ` 255 bpfPath := filepath.Join(c.MkDir(), "bpf") 256 err := main.Compile([]byte(common+seccompWhitelist), bpfPath) 257 c.Assert(err, IsNil) 258 259 // default syscall runner 260 syscallRunner := s.seccompSyscallRunner 261 262 // syscallName;arch;arg1,arg2... 263 l := strings.Split(bpfInput, ";") 264 syscallName := l[0] 265 syscallArch := "native" 266 if len(l) > 1 { 267 syscallArch = l[1] 268 } 269 270 syscallNr, err := seccomp.GetSyscallFromName(syscallName) 271 c.Assert(err, IsNil) 272 273 // Check if we want to test non-native architecture 274 // handling. Doing this via the in-kernel tests is tricky as 275 // we need a kernel that can run the architecture and a 276 // compiler that can produce the required binaries. Currently 277 // we only test amd64 running i386 here. 278 if syscallArch != "native" { 279 syscallNr, err = seccomp.GetSyscallFromNameByArch(syscallName, main.DpkgArchToScmpArch(syscallArch)) 280 c.Assert(err, IsNil) 281 282 switch syscallArch { 283 case "amd64": 284 // default syscallRunner 285 case "i386": 286 syscallRunner = s.seccompSyscallRunner + ".m32" 287 default: 288 c.Errorf("unexpected non-native arch: %s", syscallArch) 289 } 290 } 291 switch { 292 case syscallNr == -101: 293 // "socket" 294 // see libseccomp: _s390x_sock_demux(), _x86_sock_demux() 295 // the -101 is translated to 359 (socket) 296 syscallNr = 359 297 case syscallNr == -10165: 298 // "mknod" on arm64 is not available at all on arm64 299 // only "mknodat" but libseccomp will not generate a 300 // "mknodat" whitelist, it geneates a whitelist with 301 // syscall -10165 (!?!) so we cannot test this. 302 c.Skip("skipping mknod tests on arm64") 303 case syscallNr < 0: 304 c.Errorf("failed to resolve %v: %v", l[0], syscallNr) 305 return 306 } 307 308 var syscallRunnerArgs [7]string 309 syscallRunnerArgs[0] = strconv.FormatInt(int64(syscallNr), 10) 310 if len(l) > 2 { 311 args := strings.Split(l[2], ",") 312 for i := range args { 313 // init with random number argument 314 syscallArg := (uint64)(rand.Uint32()) 315 // override if the test specifies a specific number; 316 // this must match main.go:readNumber() 317 if nr, ok := main.SeccompResolver[args[i]]; ok { 318 syscallArg = nr 319 } else if nr, err := strconv.ParseUint(args[i], 10, 32); err == nil { 320 syscallArg = nr 321 } else if nr, err := strconv.ParseInt(args[i], 10, 32); err == nil { 322 syscallArg = uint64(uint32(nr)) 323 } 324 syscallRunnerArgs[i+1] = strconv.FormatUint(syscallArg, 10) 325 } 326 } 327 328 cmd := exec.Command(s.seccompBpfLoader, bpfPath, syscallRunner, syscallRunnerArgs[0], syscallRunnerArgs[1], syscallRunnerArgs[2], syscallRunnerArgs[3], syscallRunnerArgs[4], syscallRunnerArgs[5], syscallRunnerArgs[6]) 329 cmd.Stdin = os.Stdin 330 cmd.Stdout = os.Stdout 331 cmd.Stderr = os.Stderr 332 err = cmd.Run() 333 // the exit code of the test binary is either 0 or 10, everything 334 // else is unexpected (segv, strtoll failure, ...) 335 exitCode, e := osutil.ExitCode(err) 336 c.Assert(e, IsNil) 337 c.Assert(exitCode == 0 || exitCode == 10, Equals, true, Commentf("unexpected exit code: %v for %v - test setup broken", exitCode, seccompWhitelist)) 338 switch expected { 339 case Allow: 340 if err != nil { 341 c.Fatalf("unexpected error for %q (failed to run %q)", seccompWhitelist, err) 342 } 343 case Deny: 344 if err == nil { 345 c.Fatalf("unexpected success for %q %q (ran but should have failed)", seccompWhitelist, bpfInput) 346 } 347 default: 348 c.Fatalf("unknown expected result %v", expected) 349 } 350 } 351 352 func (s *snapSeccompSuite) TestUnrestricted(c *C) { 353 inp := "@unrestricted\n" 354 outPath := filepath.Join(c.MkDir(), "bpf") 355 err := main.Compile([]byte(inp), outPath) 356 c.Assert(err, IsNil) 357 358 c.Check(outPath, testutil.FileEquals, inp) 359 } 360 361 // TestCompile iterates over a range of textual seccomp whitelist rules and 362 // mocked kernel syscall input. For each rule, the test consists of compiling 363 // the rule into a bpf program and then running that program on a virtual bpf 364 // machine and comparing the bpf machine output to the specified expected 365 // output and seccomp operation. Eg: 366 // {"<rule>", "<mocked kernel input>", <seccomp result>} 367 // 368 // Eg to test that the rule 'read >=2' is allowed with 'read(2)' and 'read(3)' 369 // and denied with 'read(1)' and 'read(0)', add the following tests: 370 // {"read >=2", "read;native;2", Allow}, 371 // {"read >=2", "read;native;3", Allow}, 372 // {"read >=2", "read;native;1", main.SeccompRetKill}, 373 // {"read >=2", "read;native;0", main.SeccompRetKill}, 374 func (s *snapSeccompSuite) TestCompile(c *C) { 375 376 for _, t := range []struct { 377 seccompWhitelist string 378 bpfInput string 379 expected int 380 }{ 381 // special 382 {"@complain", "execve", Allow}, 383 384 // trivial allow 385 {"read", "read", Allow}, 386 {"read\nwrite\nexecve\n", "write", Allow}, 387 388 // trivial denial 389 {"read", "ioctl", Deny}, 390 391 // test argument filtering syntax, we currently support: 392 // >=, <=, !, <, >, | 393 // modifiers. 394 395 // reads >= 2 are ok 396 {"read >=2", "read;native;2", Allow}, 397 {"read >=2", "read;native;3", Allow}, 398 // but not reads < 2, those get killed 399 {"read >=2", "read;native;1", Deny}, 400 {"read >=2", "read;native;0", Deny}, 401 402 // reads <= 2 are ok 403 {"read <=2", "read;native;0", Allow}, 404 {"read <=2", "read;native;1", Allow}, 405 {"read <=2", "read;native;2", Allow}, 406 // but not reads >2, those get killed 407 {"read <=2", "read;native;3", Deny}, 408 {"read <=2", "read;native;4", Deny}, 409 410 // reads that are not 2 are ok 411 {"read !2", "read;native;1", Allow}, 412 {"read !2", "read;native;3", Allow}, 413 // but not 2, this gets killed 414 {"read !2", "read;native;2", Deny}, 415 416 // reads > 2 are ok 417 {"read >2", "read;native;4", Allow}, 418 {"read >2", "read;native;3", Allow}, 419 // but not reads <= 2, those get killed 420 {"read >2", "read;native;2", Deny}, 421 {"read >2", "read;native;1", Deny}, 422 423 // reads < 2 are ok 424 {"read <2", "read;native;0", Allow}, 425 {"read <2", "read;native;1", Allow}, 426 // but not reads >= 2, those get killed 427 {"read <2", "read;native;2", Deny}, 428 {"read <2", "read;native;3", Deny}, 429 430 // FIXME: test maskedEqual better 431 {"read |1", "read;native;1", Allow}, 432 {"read |1", "read;native;2", Deny}, 433 434 // exact match, reads == 2 are ok 435 {"read 2", "read;native;2", Allow}, 436 // but not those != 2 437 {"read 2", "read;native;3", Deny}, 438 {"read 2", "read;native;1", Deny}, 439 440 // test actual syscalls and their expected usage 441 {"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow}, 442 {"ioctl - TIOCSTI", "ioctl;native;-,99", Deny}, 443 {"ioctl - !TIOCSTI", "ioctl;native;-,TIOCSTI", Deny}, 444 445 // test_bad_seccomp_filter_args_clone 446 {"setns - CLONE_NEWNET", "setns;native;-,99", Deny}, 447 {"setns - CLONE_NEWNET", "setns;native;-,CLONE_NEWNET", Allow}, 448 449 // test_bad_seccomp_filter_args_mknod 450 {"mknod - |S_IFIFO", "mknod;native;-,S_IFIFO", Allow}, 451 {"mknod - |S_IFIFO", "mknod;native;-,99", Deny}, 452 453 // test_bad_seccomp_filter_args_prctl 454 {"prctl PR_CAP_AMBIENT_RAISE", "prctl;native;PR_CAP_AMBIENT_RAISE", Allow}, 455 {"prctl PR_CAP_AMBIENT_RAISE", "prctl;native;99", Deny}, 456 457 // test_bad_seccomp_filter_args_prio 458 {"setpriority PRIO_PROCESS 0 >=0", "setpriority;native;PRIO_PROCESS,0,19", Allow}, 459 {"setpriority PRIO_PROCESS 0 >=0", "setpriority;native;99", Deny}, 460 461 // test_bad_seccomp_filter_args_quotactl 462 {"quotactl Q_GETQUOTA", "quotactl;native;Q_GETQUOTA", Allow}, 463 {"quotactl Q_GETQUOTA", "quotactl;native;99", Deny}, 464 465 // test_bad_seccomp_filter_args_termios 466 {"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow}, 467 {"ioctl - TIOCSTI", "ioctl;native;-,99", Deny}, 468 469 // u:root g:root 470 {"fchown - u:root g:root", "fchown;native;-,0,0", Allow}, 471 {"fchown - u:root g:root", "fchown;native;-,99,0", Deny}, 472 {"chown - u:root g:root", "chown;native;-,0,0", Allow}, 473 {"chown - u:root g:root", "chown;native;-,99,0", Deny}, 474 475 // u:root -1 476 {"chown - u:root -1", "chown;native;-,0,-1", Allow}, 477 {"chown - u:root -1", "chown;native;-,99,-1", Deny}, 478 {"chown - -1 u:root", "chown;native;-,-1,0", Allow}, 479 {"chown - -1 u:root", "chown;native;-,99,0", Deny}, 480 {"chown - -1 -1", "chown;native;-,-1,-1", Allow}, 481 {"chown - -1 -1", "chown;native;-,99,-1", Deny}, 482 } { 483 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 484 } 485 } 486 487 // TestCompileSocket runs in a separate tests so that only this part 488 // can be skipped when "socketcall()" is used instead of "socket()". 489 // 490 // Some architectures (i386, s390x) use the "socketcall" syscall instead 491 // of "socket". This is the case on Ubuntu 14.04, 17.04, 17.10 492 func (s *snapSeccompSuite) TestCompileSocket(c *C) { 493 if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" { 494 c.Skip("14.04/i386 uses socketcall which cannot be tested here") 495 } 496 497 for _, t := range []struct { 498 seccompWhitelist string 499 bpfInput string 500 expected int 501 }{ 502 503 // test_bad_seccomp_filter_args_socket 504 {"socket AF_UNIX", "socket;native;AF_UNIX", Allow}, 505 {"socket AF_UNIX", "socket;native;99", Deny}, 506 {"socket - SOCK_STREAM", "socket;native;-,SOCK_STREAM", Allow}, 507 {"socket - SOCK_STREAM", "socket;native;-,99", Deny}, 508 {"socket AF_CONN", "socket;native;AF_CONN", Allow}, 509 {"socket AF_CONN", "socket;native;99", Deny}, 510 } { 511 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 512 } 513 514 } 515 516 func (s *snapSeccompSuite) TestCompileBadInput(c *C) { 517 for _, t := range []struct { 518 inp string 519 errMsg string 520 }{ 521 // test_bad_seccomp_filter_args_clone (various typos in input) 522 {"setns - CLONE_NEWNE", `cannot parse line: cannot parse token "CLONE_NEWNE" \(line "setns - CLONE_NEWNE"\)`}, 523 {"setns - CLONE_NEWNETT", `cannot parse line: cannot parse token "CLONE_NEWNETT" \(line "setns - CLONE_NEWNETT"\)`}, 524 {"setns - CL0NE_NEWNET", `cannot parse line: cannot parse token "CL0NE_NEWNET" \(line "setns - CL0NE_NEWNET"\)`}, 525 526 // test_bad_seccomp_filter_args_mknod (various typos in input) 527 {"mknod - |S_IFIF", `cannot parse line: cannot parse token "S_IFIF" \(line "mknod - |S_IFIF"\)`}, 528 {"mknod - |S_IFIFOO", `cannot parse line: cannot parse token "S_IFIFOO" \(line "mknod - |S_IFIFOO"\)`}, 529 {"mknod - |S_!FIFO", `cannot parse line: cannot parse token "S_IFIFO" \(line "mknod - |S_!FIFO"\)`}, 530 531 // test_bad_seccomp_filter_args_null 532 {"socket S\x00CK_STREAM", `cannot parse line: cannot parse token .*`}, 533 {"socket SOCK_STREAM\x00bad stuff", `cannot parse line: cannot parse token .*`}, 534 535 // test_bad_seccomp_filter_args 536 {"setpriority bar", `cannot parse line: cannot parse token "bar" .*`}, 537 {"setpriority -1", `cannot parse line: cannot parse token "-1" .*`}, 538 {"setpriority 0 - -1 0", `cannot parse line: cannot parse token "-1" .*`}, 539 {"setpriority --10", `cannot parse line: cannot parse token "--10" .*`}, 540 {"setpriority 0:10", `cannot parse line: cannot parse token "0:10" .*`}, 541 {"setpriority 0-10", `cannot parse line: cannot parse token "0-10" .*`}, 542 {"setpriority 0,1", `cannot parse line: cannot parse token "0,1" .*`}, 543 {"setpriority 0x0", `cannot parse line: cannot parse token "0x0" .*`}, 544 {"setpriority a1", `cannot parse line: cannot parse token "a1" .*`}, 545 {"setpriority 1a", `cannot parse line: cannot parse token "1a" .*`}, 546 {"setpriority 1-", `cannot parse line: cannot parse token "1-" .*`}, 547 {"setpriority 1\\ 2", `cannot parse line: cannot parse token "1\\\\" .*`}, 548 {"setpriority 1\\n2", `cannot parse line: cannot parse token "1\\\\n2" .*`}, 549 // 1 bigger than uint32 550 {"chown 0 4294967296", `cannot parse line: cannot parse token "4294967296" .*`}, 551 // 1 smaller than int32 552 {"chown - 0 -2147483649", `cannot parse line: cannot parse token "-2147483649" .*`}, 553 {"setpriority 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", `cannot parse line: cannot parse token "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999" .*`}, 554 {"mbind - - - - - - 7", `cannot parse line: too many arguments specified for syscall 'mbind' in line.*`}, 555 {"mbind 1 2 3 4 5 6 7", `cannot parse line: too many arguments specified for syscall 'mbind' in line.*`}, 556 // test_bad_seccomp_filter_args_prctl 557 {"prctl PR_GET_SECCOM", `cannot parse line: cannot parse token "PR_GET_SECCOM" .*`}, 558 {"prctl PR_GET_SECCOMPP", `cannot parse line: cannot parse token "PR_GET_SECCOMPP" .*`}, 559 {"prctl PR_GET_SECC0MP", `cannot parse line: cannot parse token "PR_GET_SECC0MP" .*`}, 560 {"prctl PR_CAP_AMBIENT_RAIS", `cannot parse line: cannot parse token "PR_CAP_AMBIENT_RAIS" .*`}, 561 {"prctl PR_CAP_AMBIENT_RAISEE", `cannot parse line: cannot parse token "PR_CAP_AMBIENT_RAISEE" .*`}, 562 // test_bad_seccomp_filter_args_prio 563 {"setpriority PRIO_PROCES 0 >=0", `cannot parse line: cannot parse token "PRIO_PROCES" .*`}, 564 {"setpriority PRIO_PROCESSS 0 >=0", `cannot parse line: cannot parse token "PRIO_PROCESSS" .*`}, 565 {"setpriority PRIO_PR0CESS 0 >=0", `cannot parse line: cannot parse token "PRIO_PR0CESS" .*`}, 566 // test_bad_seccomp_filter_args_quotactl 567 {"quotactl Q_GETQUOT", `cannot parse line: cannot parse token "Q_GETQUOT" .*`}, 568 {"quotactl Q_GETQUOTAA", `cannot parse line: cannot parse token "Q_GETQUOTAA" .*`}, 569 {"quotactl Q_GETQU0TA", `cannot parse line: cannot parse token "Q_GETQU0TA" .*`}, 570 // test_bad_seccomp_filter_args_socket 571 {"socket AF_UNI", `cannot parse line: cannot parse token "AF_UNI" .*`}, 572 {"socket AF_UNIXX", `cannot parse line: cannot parse token "AF_UNIXX" .*`}, 573 {"socket AF_UN!X", `cannot parse line: cannot parse token "AF_UN!X" .*`}, 574 {"socket - SOCK_STREA", `cannot parse line: cannot parse token "SOCK_STREA" .*`}, 575 {"socket - SOCK_STREAMM", `cannot parse line: cannot parse token "SOCK_STREAMM" .*`}, 576 {"socket - NETLINK_ROUT", `cannot parse line: cannot parse token "NETLINK_ROUT" .*`}, 577 {"socket - NETLINK_ROUTEE", `cannot parse line: cannot parse token "NETLINK_ROUTEE" .*`}, 578 {"socket - NETLINK_R0UTE", `cannot parse line: cannot parse token "NETLINK_R0UTE" .*`}, 579 // test_bad_seccomp_filter_args_termios 580 {"ioctl - TIOCST", `cannot parse line: cannot parse token "TIOCST" .*`}, 581 {"ioctl - TIOCSTII", `cannot parse line: cannot parse token "TIOCSTII" .*`}, 582 {"ioctl - TIOCST1", `cannot parse line: cannot parse token "TIOCST1" .*`}, 583 // ensure missing numbers are caught 584 {"setpriority >", `cannot parse line: cannot parse token ">" .*`}, 585 {"setpriority >=", `cannot parse line: cannot parse token ">=" .*`}, 586 {"setpriority <", `cannot parse line: cannot parse token "<" .*`}, 587 {"setpriority <=", `cannot parse line: cannot parse token "<=" .*`}, 588 {"setpriority |", `cannot parse line: cannot parse token "|" .*`}, 589 {"setpriority !", `cannot parse line: cannot parse token "!" .*`}, 590 591 // u:<username> 592 {"setuid :root", `cannot parse line: cannot parse token ":root" .*`}, 593 {"setuid u:", `cannot parse line: cannot parse token "u:" \(line "setuid u:"\): "" must be a valid username`}, 594 {"setuid u:!", `cannot parse line: cannot parse token "u:!" \(line "setuid u:!"\): "!" must be a valid username`}, 595 {"setuid u:b@d|npu+", `cannot parse line: cannot parse token "u:b@d|npu+" \(line "setuid u:b@d|npu+"\): "b@d|npu+" must be a valid username`}, 596 {"setuid u:snap|bad", `cannot parse line: cannot parse token "u:snap|bad" \(line "setuid u:snap|bad"\): "snap|bad" must be a valid username`}, 597 {"setuid U:root", `cannot parse line: cannot parse token "U:root" .*`}, 598 {"setuid u:nonexistent", `cannot parse line: cannot parse token "u:nonexistent" \(line "setuid u:nonexistent"\): user: unknown user nonexistent`}, 599 // g:<groupname> 600 {"setgid g:", `cannot parse line: cannot parse token "g:" \(line "setgid g:"\): "" must be a valid group name`}, 601 {"setgid g:!", `cannot parse line: cannot parse token "g:!" \(line "setgid g:!"\): "!" must be a valid group name`}, 602 {"setgid g:b@d|npu+", `cannot parse line: cannot parse token "g:b@d|npu+" \(line "setgid g:b@d|npu+"\): "b@d|npu+" must be a valid group name`}, 603 {"setgid g:snap|bad", `cannot parse line: cannot parse token "g:snap|bad" \(line "setgid g:snap|bad"\): "snap|bad" must be a valid group name`}, 604 {"setgid G:root", `cannot parse line: cannot parse token "G:root" .*`}, 605 {"setgid g:nonexistent", `cannot parse line: cannot parse token "g:nonexistent" \(line "setgid g:nonexistent"\): group: unknown group nonexistent`}, 606 } { 607 outPath := filepath.Join(c.MkDir(), "bpf") 608 err := main.Compile([]byte(t.inp), outPath) 609 c.Check(err, ErrorMatches, t.errMsg, Commentf("%q errors in unexpected ways, got: %q expected %q", t.inp, err, t.errMsg)) 610 } 611 } 612 613 // ported from test_restrictions_working_args_socket 614 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsSocket(c *C) { 615 if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" { 616 c.Skip("14.04/i386 uses socketcall which cannot be tested here") 617 } 618 619 for _, pre := range []string{"AF", "PF"} { 620 for _, i := range []string{"UNIX", "LOCAL", "INET", "INET6", "IPX", "NETLINK", "X25", "AX25", "ATMPVC", "APPLETALK", "PACKET", "ALG", "CAN", "BRIDGE", "NETROM", "ROSE", "NETBEUI", "SECURITY", "KEY", "ASH", "ECONET", "SNA", "IRDA", "PPPOX", "WANPIPE", "BLUETOOTH", "RDS", "LLC", "TIPC", "IUCV", "RXRPC", "ISDN", "PHONET", "IEEE802154", "CAIF", "NFC", "VSOCK", "MPLS", "IB"} { 621 seccompWhitelist := fmt.Sprintf("socket %s_%s", pre, i) 622 bpfInputGood := fmt.Sprintf("socket;native;%s_%s", pre, i) 623 bpfInputBad := "socket;native;99999" 624 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 625 s.runBpf(c, seccompWhitelist, bpfInputBad, Deny) 626 627 for _, j := range []string{"SOCK_STREAM", "SOCK_DGRAM", "SOCK_SEQPACKET", "SOCK_RAW", "SOCK_RDM", "SOCK_PACKET"} { 628 seccompWhitelist := fmt.Sprintf("socket %s_%s %s", pre, i, j) 629 bpfInputGood := fmt.Sprintf("socket;native;%s_%s,%s", pre, i, j) 630 bpfInputBad := fmt.Sprintf("socket;native;%s_%s,9999", pre, i) 631 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 632 s.runBpf(c, seccompWhitelist, bpfInputBad, Deny) 633 } 634 } 635 } 636 637 for _, i := range []string{"NETLINK_ROUTE", "NETLINK_USERSOCK", "NETLINK_FIREWALL", "NETLINK_SOCK_DIAG", "NETLINK_NFLOG", "NETLINK_XFRM", "NETLINK_SELINUX", "NETLINK_ISCSI", "NETLINK_AUDIT", "NETLINK_FIB_LOOKUP", "NETLINK_CONNECTOR", "NETLINK_NETFILTER", "NETLINK_IP6_FW", "NETLINK_DNRTMSG", "NETLINK_KOBJECT_UEVENT", "NETLINK_GENERIC", "NETLINK_SCSITRANSPORT", "NETLINK_ECRYPTFS", "NETLINK_RDMA", "NETLINK_CRYPTO", "NETLINK_INET_DIAG"} { 638 for _, j := range []string{"AF_NETLINK", "PF_NETLINK"} { 639 seccompWhitelist := fmt.Sprintf("socket %s - %s", j, i) 640 bpfInputGood := fmt.Sprintf("socket;native;%s,0,%s", j, i) 641 bpfInputBad := fmt.Sprintf("socket;native;%s,0,99", j) 642 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 643 s.runBpf(c, seccompWhitelist, bpfInputBad, Deny) 644 } 645 } 646 } 647 648 // ported from test_restrictions_working_args_quotactl 649 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsQuotactl(c *C) { 650 for _, arg := range []string{"Q_QUOTAON", "Q_QUOTAOFF", "Q_GETQUOTA", "Q_SETQUOTA", "Q_GETINFO", "Q_SETINFO", "Q_GETFMT", "Q_SYNC", "Q_XQUOTAON", "Q_XQUOTAOFF", "Q_XGETQUOTA", "Q_XSETQLIM", "Q_XGETQSTAT", "Q_XQUOTARM"} { 651 // good input 652 seccompWhitelist := fmt.Sprintf("quotactl %s", arg) 653 bpfInputGood := fmt.Sprintf("quotactl;native;%s", arg) 654 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 655 // bad input 656 for _, bad := range []string{"quotactl;native;99999", "read;native;"} { 657 s.runBpf(c, seccompWhitelist, bad, Deny) 658 } 659 } 660 } 661 662 // ported from test_restrictions_working_args_prctl 663 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsPrctl(c *C) { 664 for _, arg := range []string{"PR_CAP_AMBIENT", "PR_CAP_AMBIENT_RAISE", "PR_CAP_AMBIENT_LOWER", "PR_CAP_AMBIENT_IS_SET", "PR_CAP_AMBIENT_CLEAR_ALL", "PR_CAPBSET_READ", "PR_CAPBSET_DROP", "PR_SET_CHILD_SUBREAPER", "PR_GET_CHILD_SUBREAPER", "PR_SET_DUMPABLE", "PR_GET_DUMPABLE", "PR_GET_ENDIAN", "PR_SET_FPEMU", "PR_GET_FPEMU", "PR_SET_FPEXC", "PR_GET_FPEXC", "PR_SET_KEEPCAPS", "PR_GET_KEEPCAPS", "PR_MCE_KILL", "PR_MCE_KILL_GET", "PR_SET_MM", "PR_SET_MM_START_CODE", "PR_SET_MM_END_CODE", "PR_SET_MM_START_DATA", "PR_SET_MM_END_DATA", "PR_SET_MM_START_STACK", "PR_SET_MM_START_BRK", "PR_SET_MM_BRK", "PR_SET_MM_ARG_START", "PR_SET_MM_ARG_END", "PR_SET_MM_ENV_START", "PR_SET_MM_ENV_END", "PR_SET_MM_AUXV", "PR_SET_MM_EXE_FILE", "PR_MPX_ENABLE_MANAGEMENT", "PR_MPX_DISABLE_MANAGEMENT", "PR_SET_NAME", "PR_GET_NAME", "PR_SET_NO_NEW_PRIVS", "PR_GET_NO_NEW_PRIVS", "PR_SET_PDEATHSIG", "PR_GET_PDEATHSIG", "PR_SET_PTRACER", "PR_SET_SECCOMP", "PR_GET_SECCOMP", "PR_SET_SECUREBITS", "PR_GET_SECUREBITS", "PR_SET_THP_DISABLE", "PR_TASK_PERF_EVENTS_DISABLE", "PR_TASK_PERF_EVENTS_ENABLE", "PR_GET_THP_DISABLE", "PR_GET_TID_ADDRESS", "PR_SET_TIMERSLACK", "PR_GET_TIMERSLACK", "PR_SET_TIMING", "PR_GET_TIMING", "PR_SET_TSC", "PR_GET_TSC", "PR_SET_UNALIGN", "PR_GET_UNALIGN"} { 665 // good input 666 seccompWhitelist := fmt.Sprintf("prctl %s", arg) 667 bpfInputGood := fmt.Sprintf("prctl;native;%s", arg) 668 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 669 // bad input 670 for _, bad := range []string{"prctl;native;99999", "setpriority;native;"} { 671 s.runBpf(c, seccompWhitelist, bad, Deny) 672 } 673 674 if arg == "PR_CAP_AMBIENT" { 675 for _, j := range []string{"PR_CAP_AMBIENT_RAISE", "PR_CAP_AMBIENT_LOWER", "PR_CAP_AMBIENT_IS_SET", "PR_CAP_AMBIENT_CLEAR_ALL"} { 676 seccompWhitelist := fmt.Sprintf("prctl %s %s", arg, j) 677 bpfInputGood := fmt.Sprintf("prctl;native;%s,%s", arg, j) 678 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 679 for _, bad := range []string{ 680 fmt.Sprintf("prctl;native;%s,99999", arg), 681 "setpriority;native;", 682 } { 683 s.runBpf(c, seccompWhitelist, bad, Deny) 684 } 685 } 686 } 687 } 688 } 689 690 // ported from test_restrictions_working_args_clone 691 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsClone(c *C) { 692 for _, t := range []struct { 693 seccompWhitelist string 694 bpfInput string 695 expected int 696 }{ 697 // good input 698 {"setns - CLONE_NEWIPC", "setns;native;-,CLONE_NEWIPC", Allow}, 699 {"setns - CLONE_NEWNET", "setns;native;-,CLONE_NEWNET", Allow}, 700 {"setns - CLONE_NEWNS", "setns;native;-,CLONE_NEWNS", Allow}, 701 {"setns - CLONE_NEWPID", "setns;native;-,CLONE_NEWPID", Allow}, 702 {"setns - CLONE_NEWUSER", "setns;native;-,CLONE_NEWUSER", Allow}, 703 {"setns - CLONE_NEWUTS", "setns;native;-,CLONE_NEWUTS", Allow}, 704 // bad input 705 {"setns - CLONE_NEWIPC", "setns;native;-,99", Deny}, 706 {"setns - CLONE_NEWNET", "setns;native;-,99", Deny}, 707 {"setns - CLONE_NEWNS", "setns;native;-,99", Deny}, 708 {"setns - CLONE_NEWPID", "setns;native;-,99", Deny}, 709 {"setns - CLONE_NEWUSER", "setns;native;-,99", Deny}, 710 {"setns - CLONE_NEWUTS", "setns;native;-,99", Deny}, 711 } { 712 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 713 } 714 } 715 716 // ported from test_restrictions_working_args_mknod 717 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsMknod(c *C) { 718 for _, t := range []struct { 719 seccompWhitelist string 720 bpfInput string 721 expected int 722 }{ 723 // good input 724 {"mknod - S_IFREG", "mknod;native;-,S_IFREG", Allow}, 725 {"mknod - S_IFCHR", "mknod;native;-,S_IFCHR", Allow}, 726 {"mknod - S_IFBLK", "mknod;native;-,S_IFBLK", Allow}, 727 {"mknod - S_IFIFO", "mknod;native;-,S_IFIFO", Allow}, 728 {"mknod - S_IFSOCK", "mknod;native;-,S_IFSOCK", Allow}, 729 // bad input 730 {"mknod - S_IFREG", "mknod;native;-,999", Deny}, 731 {"mknod - S_IFCHR", "mknod;native;-,999", Deny}, 732 {"mknod - S_IFBLK", "mknod;native;-,999", Deny}, 733 {"mknod - S_IFIFO", "mknod;native;-,999", Deny}, 734 {"mknod - S_IFSOCK", "mknod;native;-,999", Deny}, 735 } { 736 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 737 } 738 } 739 740 // ported from test_restrictions_working_args_prio 741 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsPrio(c *C) { 742 for _, t := range []struct { 743 seccompWhitelist string 744 bpfInput string 745 expected int 746 }{ 747 // good input 748 {"setpriority PRIO_PROCESS", "setpriority;native;PRIO_PROCESS", Allow}, 749 {"setpriority PRIO_PGRP", "setpriority;native;PRIO_PGRP", Allow}, 750 {"setpriority PRIO_USER", "setpriority;native;PRIO_USER", Allow}, 751 // bad input 752 {"setpriority PRIO_PROCESS", "setpriority;native;99", Deny}, 753 {"setpriority PRIO_PGRP", "setpriority;native;99", Deny}, 754 {"setpriority PRIO_USER", "setpriority;native;99", Deny}, 755 } { 756 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 757 } 758 } 759 760 // ported from test_restrictions_working_args_termios 761 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsTermios(c *C) { 762 for _, t := range []struct { 763 seccompWhitelist string 764 bpfInput string 765 expected int 766 }{ 767 // good input 768 {"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow}, 769 // bad input 770 {"ioctl - TIOCSTI", "quotactl;native;-,99", Deny}, 771 } { 772 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 773 } 774 } 775 776 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsUidGid(c *C) { 777 // while 'root' user usually has uid 0, 'daemon' user uid may vary 778 // across distributions, best lookup the uid directly 779 daemonUid, err := osutil.FindUid("daemon") 780 781 if err != nil { 782 c.Skip("daemon user not available, perhaps we are in a buildroot jail") 783 } 784 785 for _, t := range []struct { 786 seccompWhitelist string 787 bpfInput string 788 expected int 789 }{ 790 // good input. 'root' is guaranteed to be '0' and 'daemon' uid 791 // was determined at runtime 792 {"setuid u:root", "setuid;native;0", Allow}, 793 {"setuid u:daemon", fmt.Sprintf("setuid;native;%v", daemonUid), Allow}, 794 {"setgid g:root", "setgid;native;0", Allow}, 795 {"setgid g:daemon", fmt.Sprintf("setgid;native;%v", daemonUid), Allow}, 796 // bad input 797 {"setuid u:root", "setuid;native;99", Deny}, 798 {"setuid u:daemon", "setuid;native;99", Deny}, 799 {"setgid g:root", "setgid;native;99", Deny}, 800 {"setgid g:daemon", "setgid;native;99", Deny}, 801 } { 802 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 803 } 804 } 805 806 func (s *snapSeccompSuite) TestCompatArchWorks(c *C) { 807 if !s.canCheckCompatArch { 808 c.Skip("multi-lib syscall runner not supported by this host") 809 } 810 for _, t := range []struct { 811 arch string 812 seccompWhitelist string 813 bpfInput string 814 expected int 815 }{ 816 // on amd64 we add compat i386 817 {"amd64", "read", "read;i386", Allow}, 818 {"amd64", "read", "read;amd64", Allow}, 819 {"amd64", "chown - 0 -1", "chown;i386;-,0,-1", Allow}, 820 {"amd64", "chown - 0 -1", "chown;amd64;-,0,-1", Allow}, 821 {"amd64", "chown - 0 -1", "chown;i386;-,99,-1", Deny}, 822 {"amd64", "chown - 0 -1", "chown;amd64;-,99,-1", Deny}, 823 {"amd64", "setresuid -1 -1 -1", "setresuid;i386;-1,-1,-1", Allow}, 824 {"amd64", "setresuid -1 -1 -1", "setresuid;amd64;-1,-1,-1", Allow}, 825 {"amd64", "setresuid -1 -1 -1", "setresuid;i386;-1,99,-1", Deny}, 826 {"amd64", "setresuid -1 -1 -1", "setresuid;amd64;-1,99,-1", Deny}, 827 } { 828 // It is tricky to mock the architecture here because 829 // seccomp is always adding the native arch to the seccomp 830 // filter and it will silently discard arches that have 831 // an endian mismatch: 832 // https://github.com/seccomp/libseccomp/issues/86 833 // 834 // This means we can not just 835 // main.MockArchDpkgArchitecture(t.arch) 836 // here because on endian mismatch the arch will *not* be 837 // added 838 if arch.DpkgArchitecture() == t.arch { 839 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 840 } 841 } 842 }