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