github.com/stulluk/snapd@v0.0.0-20210611110309-f6d5d5bd24b0/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" needs to be demuxed 294 switch arch.DpkgArchitecture() { 295 case "ppc64el": 296 // see libseccomp: _ppc64_syscall_demux() 297 syscallNr = 326 298 default: 299 // see libseccomp: _s390x_sock_demux(), 300 // _x86_sock_demux() the -101 is translated to 301 // 359 (socket) 302 syscallNr = 359 303 } 304 case syscallNr == -10165: 305 // "mknod" on arm64 is not available at all on arm64 306 // only "mknodat" but libseccomp will not generate a 307 // "mknodat" whitelist, it geneates a whitelist with 308 // syscall -10165 (!?!) so we cannot test this. 309 c.Skip("skipping mknod tests on arm64") 310 case syscallNr < 0: 311 c.Errorf("failed to resolve %v: %v", l[0], syscallNr) 312 return 313 } 314 315 var syscallRunnerArgs [7]string 316 syscallRunnerArgs[0] = strconv.FormatInt(int64(syscallNr), 10) 317 if len(l) > 2 { 318 args := strings.Split(l[2], ",") 319 for i := range args { 320 // init with random number argument 321 syscallArg := (uint64)(rand.Uint32()) 322 // override if the test specifies a specific number; 323 // this must match main.go:readNumber() 324 if nr, ok := main.SeccompResolver[args[i]]; ok { 325 syscallArg = nr 326 } else if nr, err := strconv.ParseUint(args[i], 10, 32); err == nil { 327 syscallArg = nr 328 } else if nr, err := strconv.ParseInt(args[i], 10, 32); err == nil { 329 syscallArg = uint64(uint32(nr)) 330 } 331 syscallRunnerArgs[i+1] = strconv.FormatUint(syscallArg, 10) 332 } 333 } 334 335 cmd := exec.Command(s.seccompBpfLoader, bpfPath, syscallRunner, syscallRunnerArgs[0], syscallRunnerArgs[1], syscallRunnerArgs[2], syscallRunnerArgs[3], syscallRunnerArgs[4], syscallRunnerArgs[5], syscallRunnerArgs[6]) 336 cmd.Stdin = os.Stdin 337 cmd.Stdout = os.Stdout 338 cmd.Stderr = os.Stderr 339 err = cmd.Run() 340 // the exit code of the test binary is either 0 or 10, everything 341 // else is unexpected (segv, strtoll failure, ...) 342 exitCode, e := osutil.ExitCode(err) 343 c.Assert(e, IsNil) 344 c.Assert(exitCode == 0 || exitCode == 10, Equals, true, Commentf("unexpected exit code: %v for %v - test setup broken", exitCode, seccompWhitelist)) 345 switch expected { 346 case Allow: 347 if err != nil { 348 c.Fatalf("unexpected error for %q (failed to run %q)", seccompWhitelist, err) 349 } 350 case Deny: 351 if err == nil { 352 c.Fatalf("unexpected success for %q %q (ran but should have failed)", seccompWhitelist, bpfInput) 353 } 354 default: 355 c.Fatalf("unknown expected result %v", expected) 356 } 357 } 358 359 func (s *snapSeccompSuite) TestUnrestricted(c *C) { 360 inp := "@unrestricted\n" 361 outPath := filepath.Join(c.MkDir(), "bpf") 362 err := main.Compile([]byte(inp), outPath) 363 c.Assert(err, IsNil) 364 365 c.Check(outPath, testutil.FileEquals, inp) 366 } 367 368 // TestCompile iterates over a range of textual seccomp whitelist rules and 369 // mocked kernel syscall input. For each rule, the test consists of compiling 370 // the rule into a bpf program and then running that program on a virtual bpf 371 // machine and comparing the bpf machine output to the specified expected 372 // output and seccomp operation. Eg: 373 // {"<rule>", "<mocked kernel input>", <seccomp result>} 374 // 375 // Eg to test that the rule 'read >=2' is allowed with 'read(2)' and 'read(3)' 376 // and denied with 'read(1)' and 'read(0)', add the following tests: 377 // {"read >=2", "read;native;2", Allow}, 378 // {"read >=2", "read;native;3", Allow}, 379 // {"read >=2", "read;native;1", main.SeccompRetKill}, 380 // {"read >=2", "read;native;0", main.SeccompRetKill}, 381 func (s *snapSeccompSuite) TestCompile(c *C) { 382 383 for _, t := range []struct { 384 seccompWhitelist string 385 bpfInput string 386 expected int 387 }{ 388 // special 389 {"@complain", "execve", Allow}, 390 391 // trivial allow 392 {"read", "read", Allow}, 393 {"read\nwrite\nexecve\n", "write", Allow}, 394 395 // trivial denial 396 {"read", "ioctl", Deny}, 397 398 // test argument filtering syntax, we currently support: 399 // >=, <=, !, <, >, | 400 // modifiers. 401 402 // reads >= 2 are ok 403 {"read >=2", "read;native;2", Allow}, 404 {"read >=2", "read;native;3", Allow}, 405 // but not reads < 2, those get killed 406 {"read >=2", "read;native;1", Deny}, 407 {"read >=2", "read;native;0", Deny}, 408 409 // reads <= 2 are ok 410 {"read <=2", "read;native;0", Allow}, 411 {"read <=2", "read;native;1", Allow}, 412 {"read <=2", "read;native;2", Allow}, 413 // but not reads >2, those get killed 414 {"read <=2", "read;native;3", Deny}, 415 {"read <=2", "read;native;4", Deny}, 416 417 // reads that are not 2 are ok 418 {"read !2", "read;native;1", Allow}, 419 {"read !2", "read;native;3", Allow}, 420 // but not 2, this gets killed 421 {"read !2", "read;native;2", Deny}, 422 423 // reads > 2 are ok 424 {"read >2", "read;native;4", Allow}, 425 {"read >2", "read;native;3", Allow}, 426 // but not reads <= 2, those get killed 427 {"read >2", "read;native;2", Deny}, 428 {"read >2", "read;native;1", Deny}, 429 430 // reads < 2 are ok 431 {"read <2", "read;native;0", Allow}, 432 {"read <2", "read;native;1", Allow}, 433 // but not reads >= 2, those get killed 434 {"read <2", "read;native;2", Deny}, 435 {"read <2", "read;native;3", Deny}, 436 437 // FIXME: test maskedEqual better 438 {"read |1", "read;native;1", Allow}, 439 {"read |1", "read;native;2", Deny}, 440 441 // exact match, reads == 2 are ok 442 {"read 2", "read;native;2", Allow}, 443 // but not those != 2 444 {"read 2", "read;native;3", Deny}, 445 {"read 2", "read;native;1", Deny}, 446 447 // test actual syscalls and their expected usage 448 {"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow}, 449 {"ioctl - TIOCSTI", "ioctl;native;-,99", Deny}, 450 {"ioctl - !TIOCSTI", "ioctl;native;-,TIOCSTI", Deny}, 451 452 // test_bad_seccomp_filter_args_clone 453 {"setns - CLONE_NEWNET", "setns;native;-,99", Deny}, 454 {"setns - CLONE_NEWNET", "setns;native;-,CLONE_NEWNET", Allow}, 455 456 // test_bad_seccomp_filter_args_mknod 457 {"mknod - |S_IFIFO", "mknod;native;-,S_IFIFO", Allow}, 458 {"mknod - |S_IFIFO", "mknod;native;-,99", Deny}, 459 460 // test_bad_seccomp_filter_args_prctl 461 {"prctl PR_CAP_AMBIENT_RAISE", "prctl;native;PR_CAP_AMBIENT_RAISE", Allow}, 462 {"prctl PR_CAP_AMBIENT_RAISE", "prctl;native;99", Deny}, 463 464 // test_bad_seccomp_filter_args_prio 465 {"setpriority PRIO_PROCESS 0 >=0", "setpriority;native;PRIO_PROCESS,0,19", Allow}, 466 {"setpriority PRIO_PROCESS 0 >=0", "setpriority;native;99", Deny}, 467 468 // test_bad_seccomp_filter_args_quotactl 469 {"quotactl Q_GETQUOTA", "quotactl;native;Q_GETQUOTA", Allow}, 470 {"quotactl Q_GETQUOTA", "quotactl;native;99", Deny}, 471 472 // test_bad_seccomp_filter_args_termios 473 {"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow}, 474 {"ioctl - TIOCSTI", "ioctl;native;-,99", Deny}, 475 476 // u:root g:root 477 {"fchown - u:root g:root", "fchown;native;-,0,0", Allow}, 478 {"fchown - u:root g:root", "fchown;native;-,99,0", Deny}, 479 {"chown - u:root g:root", "chown;native;-,0,0", Allow}, 480 {"chown - u:root g:root", "chown;native;-,99,0", Deny}, 481 482 // u:root -1 483 {"chown - u:root -1", "chown;native;-,0,-1", Allow}, 484 {"chown - u:root -1", "chown;native;-,99,-1", Deny}, 485 {"chown - -1 u:root", "chown;native;-,-1,0", Allow}, 486 {"chown - -1 u:root", "chown;native;-,99,0", Deny}, 487 {"chown - -1 -1", "chown;native;-,-1,-1", Allow}, 488 {"chown - -1 -1", "chown;native;-,99,-1", Deny}, 489 } { 490 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 491 } 492 } 493 494 // TestCompileSocket runs in a separate tests so that only this part 495 // can be skipped when "socketcall()" is used instead of "socket()". 496 // 497 // Some architectures (i386, s390x) use the "socketcall" syscall instead 498 // of "socket". This is the case on Ubuntu 14.04, 17.04, 17.10 499 func (s *snapSeccompSuite) TestCompileSocket(c *C) { 500 if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" { 501 c.Skip("14.04/i386 uses socketcall which cannot be tested here") 502 } 503 504 for _, t := range []struct { 505 seccompWhitelist string 506 bpfInput string 507 expected int 508 }{ 509 510 // test_bad_seccomp_filter_args_socket 511 {"socket AF_UNIX", "socket;native;AF_UNIX", Allow}, 512 {"socket AF_UNIX", "socket;native;99", Deny}, 513 {"socket - SOCK_STREAM", "socket;native;-,SOCK_STREAM", Allow}, 514 {"socket - SOCK_STREAM", "socket;native;-,99", Deny}, 515 {"socket AF_CONN", "socket;native;AF_CONN", Allow}, 516 {"socket AF_CONN", "socket;native;99", Deny}, 517 } { 518 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 519 } 520 521 } 522 523 func (s *snapSeccompSuite) TestCompileBadInput(c *C) { 524 for _, t := range []struct { 525 inp string 526 errMsg string 527 }{ 528 // test_bad_seccomp_filter_args_clone (various typos in input) 529 {"setns - CLONE_NEWNE", `cannot parse line: cannot parse token "CLONE_NEWNE" \(line "setns - CLONE_NEWNE"\)`}, 530 {"setns - CLONE_NEWNETT", `cannot parse line: cannot parse token "CLONE_NEWNETT" \(line "setns - CLONE_NEWNETT"\)`}, 531 {"setns - CL0NE_NEWNET", `cannot parse line: cannot parse token "CL0NE_NEWNET" \(line "setns - CL0NE_NEWNET"\)`}, 532 533 // test_bad_seccomp_filter_args_mknod (various typos in input) 534 {"mknod - |S_IFIF", `cannot parse line: cannot parse token "S_IFIF" \(line "mknod - |S_IFIF"\)`}, 535 {"mknod - |S_IFIFOO", `cannot parse line: cannot parse token "S_IFIFOO" \(line "mknod - |S_IFIFOO"\)`}, 536 {"mknod - |S_!FIFO", `cannot parse line: cannot parse token "S_IFIFO" \(line "mknod - |S_!FIFO"\)`}, 537 538 // test_bad_seccomp_filter_args_null 539 {"socket S\x00CK_STREAM", `cannot parse line: cannot parse token .*`}, 540 {"socket SOCK_STREAM\x00bad stuff", `cannot parse line: cannot parse token .*`}, 541 542 // test_bad_seccomp_filter_args 543 {"setpriority bar", `cannot parse line: cannot parse token "bar" .*`}, 544 {"setpriority -1", `cannot parse line: cannot parse token "-1" .*`}, 545 {"setpriority 0 - -1 0", `cannot parse line: cannot parse token "-1" .*`}, 546 {"setpriority --10", `cannot parse line: cannot parse token "--10" .*`}, 547 {"setpriority 0:10", `cannot parse line: cannot parse token "0:10" .*`}, 548 {"setpriority 0-10", `cannot parse line: cannot parse token "0-10" .*`}, 549 {"setpriority 0,1", `cannot parse line: cannot parse token "0,1" .*`}, 550 {"setpriority 0x0", `cannot parse line: cannot parse token "0x0" .*`}, 551 {"setpriority a1", `cannot parse line: cannot parse token "a1" .*`}, 552 {"setpriority 1a", `cannot parse line: cannot parse token "1a" .*`}, 553 {"setpriority 1-", `cannot parse line: cannot parse token "1-" .*`}, 554 {"setpriority 1\\ 2", `cannot parse line: cannot parse token "1\\\\" .*`}, 555 {"setpriority 1\\n2", `cannot parse line: cannot parse token "1\\\\n2" .*`}, 556 // 1 bigger than uint32 557 {"chown 0 4294967296", `cannot parse line: cannot parse token "4294967296" .*`}, 558 // 1 smaller than int32 559 {"chown - 0 -2147483649", `cannot parse line: cannot parse token "-2147483649" .*`}, 560 {"setpriority 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", `cannot parse line: cannot parse token "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999" .*`}, 561 {"mbind - - - - - - 7", `cannot parse line: too many arguments specified for syscall 'mbind' in line.*`}, 562 {"mbind 1 2 3 4 5 6 7", `cannot parse line: too many arguments specified for syscall 'mbind' in line.*`}, 563 // test_bad_seccomp_filter_args_prctl 564 {"prctl PR_GET_SECCOM", `cannot parse line: cannot parse token "PR_GET_SECCOM" .*`}, 565 {"prctl PR_GET_SECCOMPP", `cannot parse line: cannot parse token "PR_GET_SECCOMPP" .*`}, 566 {"prctl PR_GET_SECC0MP", `cannot parse line: cannot parse token "PR_GET_SECC0MP" .*`}, 567 {"prctl PR_CAP_AMBIENT_RAIS", `cannot parse line: cannot parse token "PR_CAP_AMBIENT_RAIS" .*`}, 568 {"prctl PR_CAP_AMBIENT_RAISEE", `cannot parse line: cannot parse token "PR_CAP_AMBIENT_RAISEE" .*`}, 569 // test_bad_seccomp_filter_args_prio 570 {"setpriority PRIO_PROCES 0 >=0", `cannot parse line: cannot parse token "PRIO_PROCES" .*`}, 571 {"setpriority PRIO_PROCESSS 0 >=0", `cannot parse line: cannot parse token "PRIO_PROCESSS" .*`}, 572 {"setpriority PRIO_PR0CESS 0 >=0", `cannot parse line: cannot parse token "PRIO_PR0CESS" .*`}, 573 // test_bad_seccomp_filter_args_quotactl 574 {"quotactl Q_GETQUOT", `cannot parse line: cannot parse token "Q_GETQUOT" .*`}, 575 {"quotactl Q_GETQUOTAA", `cannot parse line: cannot parse token "Q_GETQUOTAA" .*`}, 576 {"quotactl Q_GETQU0TA", `cannot parse line: cannot parse token "Q_GETQU0TA" .*`}, 577 // test_bad_seccomp_filter_args_socket 578 {"socket AF_UNI", `cannot parse line: cannot parse token "AF_UNI" .*`}, 579 {"socket AF_UNIXX", `cannot parse line: cannot parse token "AF_UNIXX" .*`}, 580 {"socket AF_UN!X", `cannot parse line: cannot parse token "AF_UN!X" .*`}, 581 {"socket - SOCK_STREA", `cannot parse line: cannot parse token "SOCK_STREA" .*`}, 582 {"socket - SOCK_STREAMM", `cannot parse line: cannot parse token "SOCK_STREAMM" .*`}, 583 {"socket - NETLINK_ROUT", `cannot parse line: cannot parse token "NETLINK_ROUT" .*`}, 584 {"socket - NETLINK_ROUTEE", `cannot parse line: cannot parse token "NETLINK_ROUTEE" .*`}, 585 {"socket - NETLINK_R0UTE", `cannot parse line: cannot parse token "NETLINK_R0UTE" .*`}, 586 // test_bad_seccomp_filter_args_termios 587 {"ioctl - TIOCST", `cannot parse line: cannot parse token "TIOCST" .*`}, 588 {"ioctl - TIOCSTII", `cannot parse line: cannot parse token "TIOCSTII" .*`}, 589 {"ioctl - TIOCST1", `cannot parse line: cannot parse token "TIOCST1" .*`}, 590 // ensure missing numbers are caught 591 {"setpriority >", `cannot parse line: cannot parse token ">" .*`}, 592 {"setpriority >=", `cannot parse line: cannot parse token ">=" .*`}, 593 {"setpriority <", `cannot parse line: cannot parse token "<" .*`}, 594 {"setpriority <=", `cannot parse line: cannot parse token "<=" .*`}, 595 {"setpriority |", `cannot parse line: cannot parse token "|" .*`}, 596 {"setpriority !", `cannot parse line: cannot parse token "!" .*`}, 597 598 // u:<username> 599 {"setuid :root", `cannot parse line: cannot parse token ":root" .*`}, 600 {"setuid u:", `cannot parse line: cannot parse token "u:" \(line "setuid u:"\): "" must be a valid username`}, 601 {"setuid u:!", `cannot parse line: cannot parse token "u:!" \(line "setuid u:!"\): "!" must be a valid username`}, 602 {"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`}, 603 {"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`}, 604 {"setuid U:root", `cannot parse line: cannot parse token "U:root" .*`}, 605 {"setuid u:nonexistent", `cannot parse line: cannot parse token "u:nonexistent" \(line "setuid u:nonexistent"\): user: unknown user nonexistent`}, 606 // g:<groupname> 607 {"setgid g:", `cannot parse line: cannot parse token "g:" \(line "setgid g:"\): "" must be a valid group name`}, 608 {"setgid g:!", `cannot parse line: cannot parse token "g:!" \(line "setgid g:!"\): "!" must be a valid group name`}, 609 {"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`}, 610 {"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`}, 611 {"setgid G:root", `cannot parse line: cannot parse token "G:root" .*`}, 612 {"setgid g:nonexistent", `cannot parse line: cannot parse token "g:nonexistent" \(line "setgid g:nonexistent"\): group: unknown group nonexistent`}, 613 } { 614 outPath := filepath.Join(c.MkDir(), "bpf") 615 err := main.Compile([]byte(t.inp), outPath) 616 c.Check(err, ErrorMatches, t.errMsg, Commentf("%q errors in unexpected ways, got: %q expected %q", t.inp, err, t.errMsg)) 617 } 618 } 619 620 // ported from test_restrictions_working_args_socket 621 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsSocket(c *C) { 622 if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" { 623 c.Skip("14.04/i386 uses socketcall which cannot be tested here") 624 } 625 626 for _, pre := range []string{"AF", "PF"} { 627 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"} { 628 seccompWhitelist := fmt.Sprintf("socket %s_%s", pre, i) 629 bpfInputGood := fmt.Sprintf("socket;native;%s_%s", pre, i) 630 bpfInputBad := "socket;native;99999" 631 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 632 s.runBpf(c, seccompWhitelist, bpfInputBad, Deny) 633 634 for _, j := range []string{"SOCK_STREAM", "SOCK_DGRAM", "SOCK_SEQPACKET", "SOCK_RAW", "SOCK_RDM", "SOCK_PACKET"} { 635 seccompWhitelist := fmt.Sprintf("socket %s_%s %s", pre, i, j) 636 bpfInputGood := fmt.Sprintf("socket;native;%s_%s,%s", pre, i, j) 637 bpfInputBad := fmt.Sprintf("socket;native;%s_%s,9999", pre, i) 638 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 639 s.runBpf(c, seccompWhitelist, bpfInputBad, Deny) 640 } 641 } 642 } 643 644 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"} { 645 for _, j := range []string{"AF_NETLINK", "PF_NETLINK"} { 646 seccompWhitelist := fmt.Sprintf("socket %s - %s", j, i) 647 bpfInputGood := fmt.Sprintf("socket;native;%s,0,%s", j, i) 648 bpfInputBad := fmt.Sprintf("socket;native;%s,0,99", j) 649 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 650 s.runBpf(c, seccompWhitelist, bpfInputBad, Deny) 651 } 652 } 653 } 654 655 // ported from test_restrictions_working_args_quotactl 656 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsQuotactl(c *C) { 657 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"} { 658 // good input 659 seccompWhitelist := fmt.Sprintf("quotactl %s", arg) 660 bpfInputGood := fmt.Sprintf("quotactl;native;%s", arg) 661 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 662 // bad input 663 for _, bad := range []string{"quotactl;native;99999", "read;native;"} { 664 s.runBpf(c, seccompWhitelist, bad, Deny) 665 } 666 } 667 } 668 669 // ported from test_restrictions_working_args_prctl 670 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsPrctl(c *C) { 671 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"} { 672 // good input 673 seccompWhitelist := fmt.Sprintf("prctl %s", arg) 674 bpfInputGood := fmt.Sprintf("prctl;native;%s", arg) 675 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 676 // bad input 677 for _, bad := range []string{"prctl;native;99999", "setpriority;native;"} { 678 s.runBpf(c, seccompWhitelist, bad, Deny) 679 } 680 681 if arg == "PR_CAP_AMBIENT" { 682 for _, j := range []string{"PR_CAP_AMBIENT_RAISE", "PR_CAP_AMBIENT_LOWER", "PR_CAP_AMBIENT_IS_SET", "PR_CAP_AMBIENT_CLEAR_ALL"} { 683 seccompWhitelist := fmt.Sprintf("prctl %s %s", arg, j) 684 bpfInputGood := fmt.Sprintf("prctl;native;%s,%s", arg, j) 685 s.runBpf(c, seccompWhitelist, bpfInputGood, Allow) 686 for _, bad := range []string{ 687 fmt.Sprintf("prctl;native;%s,99999", arg), 688 "setpriority;native;", 689 } { 690 s.runBpf(c, seccompWhitelist, bad, Deny) 691 } 692 } 693 } 694 } 695 } 696 697 // ported from test_restrictions_working_args_clone 698 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsClone(c *C) { 699 for _, t := range []struct { 700 seccompWhitelist string 701 bpfInput string 702 expected int 703 }{ 704 // good input 705 {"setns - CLONE_NEWIPC", "setns;native;-,CLONE_NEWIPC", Allow}, 706 {"setns - CLONE_NEWNET", "setns;native;-,CLONE_NEWNET", Allow}, 707 {"setns - CLONE_NEWNS", "setns;native;-,CLONE_NEWNS", Allow}, 708 {"setns - CLONE_NEWPID", "setns;native;-,CLONE_NEWPID", Allow}, 709 {"setns - CLONE_NEWUSER", "setns;native;-,CLONE_NEWUSER", Allow}, 710 {"setns - CLONE_NEWUTS", "setns;native;-,CLONE_NEWUTS", Allow}, 711 // bad input 712 {"setns - CLONE_NEWIPC", "setns;native;-,99", Deny}, 713 {"setns - CLONE_NEWNET", "setns;native;-,99", Deny}, 714 {"setns - CLONE_NEWNS", "setns;native;-,99", Deny}, 715 {"setns - CLONE_NEWPID", "setns;native;-,99", Deny}, 716 {"setns - CLONE_NEWUSER", "setns;native;-,99", Deny}, 717 {"setns - CLONE_NEWUTS", "setns;native;-,99", Deny}, 718 } { 719 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 720 } 721 } 722 723 // ported from test_restrictions_working_args_mknod 724 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsMknod(c *C) { 725 for _, t := range []struct { 726 seccompWhitelist string 727 bpfInput string 728 expected int 729 }{ 730 // good input 731 {"mknod - S_IFREG", "mknod;native;-,S_IFREG", Allow}, 732 {"mknod - S_IFCHR", "mknod;native;-,S_IFCHR", Allow}, 733 {"mknod - S_IFBLK", "mknod;native;-,S_IFBLK", Allow}, 734 {"mknod - S_IFIFO", "mknod;native;-,S_IFIFO", Allow}, 735 {"mknod - S_IFSOCK", "mknod;native;-,S_IFSOCK", Allow}, 736 // bad input 737 {"mknod - S_IFREG", "mknod;native;-,999", Deny}, 738 {"mknod - S_IFCHR", "mknod;native;-,999", Deny}, 739 {"mknod - S_IFBLK", "mknod;native;-,999", Deny}, 740 {"mknod - S_IFIFO", "mknod;native;-,999", Deny}, 741 {"mknod - S_IFSOCK", "mknod;native;-,999", Deny}, 742 } { 743 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 744 } 745 } 746 747 // ported from test_restrictions_working_args_prio 748 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsPrio(c *C) { 749 for _, t := range []struct { 750 seccompWhitelist string 751 bpfInput string 752 expected int 753 }{ 754 // good input 755 {"setpriority PRIO_PROCESS", "setpriority;native;PRIO_PROCESS", Allow}, 756 {"setpriority PRIO_PGRP", "setpriority;native;PRIO_PGRP", Allow}, 757 {"setpriority PRIO_USER", "setpriority;native;PRIO_USER", Allow}, 758 // bad input 759 {"setpriority PRIO_PROCESS", "setpriority;native;99", Deny}, 760 {"setpriority PRIO_PGRP", "setpriority;native;99", Deny}, 761 {"setpriority PRIO_USER", "setpriority;native;99", Deny}, 762 } { 763 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 764 } 765 } 766 767 // ported from test_restrictions_working_args_termios 768 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsTermios(c *C) { 769 for _, t := range []struct { 770 seccompWhitelist string 771 bpfInput string 772 expected int 773 }{ 774 // good input 775 {"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow}, 776 // bad input 777 {"ioctl - TIOCSTI", "quotactl;native;-,99", Deny}, 778 } { 779 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 780 } 781 } 782 783 func (s *snapSeccompSuite) TestRestrictionsWorkingArgsUidGid(c *C) { 784 // while 'root' user usually has uid 0, 'daemon' user uid may vary 785 // across distributions, best lookup the uid directly 786 daemonUid, err := osutil.FindUid("daemon") 787 788 if err != nil { 789 c.Skip("daemon user not available, perhaps we are in a buildroot jail") 790 } 791 792 for _, t := range []struct { 793 seccompWhitelist string 794 bpfInput string 795 expected int 796 }{ 797 // good input. 'root' is guaranteed to be '0' and 'daemon' uid 798 // was determined at runtime 799 {"setuid u:root", "setuid;native;0", Allow}, 800 {"setuid u:daemon", fmt.Sprintf("setuid;native;%v", daemonUid), Allow}, 801 {"setgid g:root", "setgid;native;0", Allow}, 802 {"setgid g:daemon", fmt.Sprintf("setgid;native;%v", daemonUid), Allow}, 803 // bad input 804 {"setuid u:root", "setuid;native;99", Deny}, 805 {"setuid u:daemon", "setuid;native;99", Deny}, 806 {"setgid g:root", "setgid;native;99", Deny}, 807 {"setgid g:daemon", "setgid;native;99", Deny}, 808 } { 809 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 810 } 811 } 812 813 func (s *snapSeccompSuite) TestCompatArchWorks(c *C) { 814 if !s.canCheckCompatArch { 815 c.Skip("multi-lib syscall runner not supported by this host") 816 } 817 for _, t := range []struct { 818 arch string 819 seccompWhitelist string 820 bpfInput string 821 expected int 822 }{ 823 // on amd64 we add compat i386 824 {"amd64", "read", "read;i386", Allow}, 825 {"amd64", "read", "read;amd64", Allow}, 826 {"amd64", "chown - 0 -1", "chown;i386;-,0,-1", Allow}, 827 {"amd64", "chown - 0 -1", "chown;amd64;-,0,-1", Allow}, 828 {"amd64", "chown - 0 -1", "chown;i386;-,99,-1", Deny}, 829 {"amd64", "chown - 0 -1", "chown;amd64;-,99,-1", Deny}, 830 {"amd64", "setresuid -1 -1 -1", "setresuid;i386;-1,-1,-1", Allow}, 831 {"amd64", "setresuid -1 -1 -1", "setresuid;amd64;-1,-1,-1", Allow}, 832 {"amd64", "setresuid -1 -1 -1", "setresuid;i386;-1,99,-1", Deny}, 833 {"amd64", "setresuid -1 -1 -1", "setresuid;amd64;-1,99,-1", Deny}, 834 } { 835 // It is tricky to mock the architecture here because 836 // seccomp is always adding the native arch to the seccomp 837 // filter and it will silently discard arches that have 838 // an endian mismatch: 839 // https://github.com/seccomp/libseccomp/issues/86 840 // 841 // This means we can not just 842 // main.MockArchDpkgArchitecture(t.arch) 843 // here because on endian mismatch the arch will *not* be 844 // added 845 if arch.DpkgArchitecture() == t.arch { 846 s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) 847 } 848 } 849 }