Compare commits
79 Commits
d847d0f996
...
next
Author | SHA1 | Date | |
---|---|---|---|
40d23af355 | |||
b5f83499f3 | |||
ff60ec227d | |||
e711a1d53a | |||
6628bf4fb7 | |||
3fa73b0b97 | |||
8f38dc4480 | |||
42d44b0cc1 | |||
bd3641981c | |||
bbbdfc44da | |||
2dc61828f1 | |||
cdc265cedf | |||
91858efa51 | |||
88995d214d | |||
6eb47daf84 | |||
8bf87717a5 | |||
bcaefffbe8 | |||
ed5098f2c6 | |||
ea66ef76eb | |||
66def7a28f | |||
dbf8e87440 | |||
98421fab90 | |||
70c3fef500 | |||
69829374c7 | |||
005851c645 | |||
95fa11e928 | |||
97e2025758 | |||
8cfb73568a | |||
e7a5ba7f7f | |||
e52eda186b | |||
90ed5bbae9 | |||
48b6de9036 | |||
93acb13929 | |||
9247a6636b | |||
4a3ac8e0bc | |||
ed54575b89 | |||
0caff45600 | |||
080c0e53c2 | |||
4adc13215b | |||
bf29edf213 | |||
68bfd7e66c | |||
58bc50db61 | |||
1e63fa75ef | |||
6c44c88397 | |||
3780509078 | |||
fd4dfb12f0 | |||
a9e6b3ee67 | |||
3b61e90761 | |||
0e27b19999 | |||
ff70142e04 | |||
4824c6eaa9 | |||
9048a3b4fe | |||
0b54e73ff4 | |||
b2306299d5 | |||
55b43fdaac | |||
6420ca1b40 | |||
98c76089de | |||
631980b775 | |||
0be081c55d | |||
ca0f82790c | |||
77adf09d34 | |||
bcab0377f1 | |||
b469a82eec | |||
6711b394d9 | |||
9abbc7510c | |||
029762e894 | |||
6b513f8339 | |||
d2357ac676 | |||
0b0dda0de1 | |||
7115ef8b4d | |||
15a6850023 | |||
48deab0dde | |||
ce7eb57998 | |||
3407fded04 | |||
1b4c5477a5 | |||
756b0fb421 | |||
d150c2ecd9 | |||
435bcefa48 | |||
2a4cee2ece |
20
Makefile
20
Makefile
@ -1,17 +1,27 @@
|
|||||||
prefix = /usr/local
|
prefix = /usr/local
|
||||||
bindir = $(prefix)/bin
|
bindir = $(prefix)/bin
|
||||||
CFLAGS = -std=c99 -Wall -Wextra -pedantic
|
CFLAGS = -std=c99 -Wall -Wextra -pedantic
|
||||||
|
CXXFLAGS = -std=c++20 -Wall -Wextra -pedantic
|
||||||
|
|
||||||
.DEFAULT_GOAL := test
|
.DEFAULT_GOAL := tests
|
||||||
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f test
|
rm -f test exile.o testcpp
|
||||||
|
|
||||||
test: test.c
|
|
||||||
$(CC) test.c -g $(CFLAGS) -o test
|
|
||||||
|
|
||||||
check: test
|
exile.o: exile.c exile.h
|
||||||
|
$(CC) -c exile.c -g $(CFLAGS) -o exile.o
|
||||||
|
|
||||||
|
test: test.c exile.h exile.o
|
||||||
|
$(CC) test.c exile.o -g $(CFLAGS) -o test
|
||||||
|
|
||||||
|
testcpp: test.cpp exile.h exile.hpp exile.o
|
||||||
|
$(CXX) test.cpp exile.o -g $(CXXFLAGS) -o testcpp
|
||||||
|
|
||||||
|
tests: test testcpp
|
||||||
|
|
||||||
|
check: tests
|
||||||
./test.sh
|
./test.sh
|
||||||
|
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
|
214
README.md
214
README.md
@ -1,55 +1,219 @@
|
|||||||
# qssb.h (quite simple sandbox)
|
# exile.h
|
||||||
`qssb.h` is a simple header-only library that provides an interface to sandbox processes on Linux. Using Seccomp and Linux Namespaces for that purpose requires some knowledge of annoying details which this library aims to abstract away as much as possible, when reasonable. Hence, the goal is to provide a convenient way for processes to restrict themselves in order to mitigate the effect of exploits. Currently, it utilizes technologies like Seccomp, Namespaces and Landlock to this end.
|
`exile.h` provides an API for processes on Linux to easily isolate themselves for exploit mitigation purposes. exile.h makes it simpler for developers to use existing technologies such as Seccomp and Linux Namespaces. Those generally require knowledge of details and are not trivial for developers to employ, which prevents a more widespread adoption.
|
||||||
|
|
||||||
|
The following section offers small examples. Then the motivation is explained in more detail.
|
||||||
|
Proper API documentation will be maintained in other files.
|
||||||
|
|
||||||
|
## Quick demo
|
||||||
|
This section quickly demonstrates the simplicity of the API. It serves as an overview to get
|
||||||
|
a first impression.
|
||||||
|
|
||||||
|
system() is used to keep the example C code short. It also demonstrates that subprocesses are also subject to restrictions imposed by exile.h.
|
||||||
|
|
||||||
|
While the examples show different features separately, it is generally possible to combine those.
|
||||||
|
|
||||||
|
### Filesystem isolation
|
||||||
|
```c
|
||||||
|
#include "exile.h"
|
||||||
|
#include <assert.h>
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
system("echo test > /home/user/testfile");
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ, "/home/user");
|
||||||
|
exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ | EXILE_FS_ALLOW_ALL_WRITE, "/tmp");
|
||||||
|
int ret = exile_enable_policy(policy);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
int fd = open("/home/user/test", O_CREAT | O_WRONLY | O_TRUNC, 0600);
|
||||||
|
assert(fd == -1);
|
||||||
|
fd = open("/home/user/testfile", O_RDONLY);
|
||||||
|
//use fd
|
||||||
|
assert(fd != -1);
|
||||||
|
fd = open("/tmp/testfile", O_CREAT | O_WRONLY | O_TRUNC, 0600);
|
||||||
|
//use fd
|
||||||
|
assert(fd != -1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The assert() calls won't be fired, consistent with the policy.
|
||||||
|
|
||||||
|
### System call policies / vows
|
||||||
|
exile.h allows specifying which syscalls are permitted or denied. In the following example,
|
||||||
|
'ls' is never executed, as the specified "vows" do not allow the execve() system call. The
|
||||||
|
process will be killed.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "exile.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
policy->vow_promises = exile_vows_from_str("stdio rpath wpath cpath");
|
||||||
|
exile_enable_policy(policy);
|
||||||
|
printf("Trying to execute...");
|
||||||
|
execlp("/bin/ls", "ls", "/", NULL);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Isolation from network
|
||||||
|
exile offers a quick way to isolate a process from the default network namespace.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "exile.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
policy->namespace_options |= EXILE_UNSHARE_NETWORK;
|
||||||
|
int ret = exile_enable_policy(policy);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
system("curl -I https://evil.tld");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Produces ```curl: (6) Could not resolve host: evil.tld```. For example, this is useful for subprocesses which do not need
|
||||||
|
network access, but perform tasks such as parsing user-supplied file formats.
|
||||||
|
|
||||||
|
### Isolation of single functions
|
||||||
|
Currently, work is being done that hopefully will allow isolation of individual function calls in a mostly pain-free manner.
|
||||||
|
|
||||||
|
Consider the following C++ code:
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include "exile.hpp"
|
||||||
|
std::string cat(std::string path)
|
||||||
|
{
|
||||||
|
std::fstream f1;
|
||||||
|
f1.open(path.c_str(), std::ios::in);
|
||||||
|
std::string content;
|
||||||
|
std::string line;
|
||||||
|
while(getline(f1, line)) {
|
||||||
|
content += line + "\n";
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
policy->vow_promises = exile_vows_from_str("stdio rpath");
|
||||||
|
|
||||||
|
std::string content = exile_launch<std::string>(policy, cat, "/etc/hosts");
|
||||||
|
std::cout << content;
|
||||||
|
|
||||||
|
policy = exile_init_policy();
|
||||||
|
policy->vow_promises = exile_vows_from_str("stdio");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
content = exile_launch<std::string>(policy, cat, "/etc/hosts");
|
||||||
|
std::cout << content;
|
||||||
|
}
|
||||||
|
catch(std::exception &e)
|
||||||
|
{
|
||||||
|
std::cout << "launch failure: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We execute "cat()". The first call succeeds. In the second, we get an exception, because
|
||||||
|
the subprocess "cat()" was launched in violated the policy (missing "rpath" vow).
|
||||||
|
|
||||||
|
Naturally, there is a performance overhead. Certain challenges remain, such as the fact
|
||||||
|
that being executed in a subprocess, we operate on copies, so handling references
|
||||||
|
is not something that has been given much thought. There is also the fact
|
||||||
|
that clone()ing from threads opens a can of worms, particularly with locks. Hence, exile_launch()
|
||||||
|
is best avoided in multi-threaded contexts.
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
No release yet, expiremental, API is unstable, builds will break on updates of this library.
|
No release yet, experimental, API is unstable, builds will break on updates of this library.
|
||||||
|
|
||||||
Currently, it's mainly evolving according to the needs of my other projects.
|
Currently, it's mainly evolving from the needs of my other projects which use exile.h.
|
||||||
|
|
||||||
|
## Motivation and Background
|
||||||
|
exile.h unlocks existing Linux mechanisms to facilitate isolation of processes from resources. Limiting the scope of what programs can do helps defending the rest of the system when a process gets under attacker's control (when classic mitigations such as ASLR etc. failed). To this end, OpenBSD has the pledge() and unveil() functions available. Those functions are helpful mitigation mechanisms, but such accessible ways are unfortunately not readily available on Linux. This is where exile.h steps in.
|
||||||
|
|
||||||
|
Seccomp allows restricting the system calls available to a process and thus decrease the systems attack surface, but it generally is not easy to use. Requiring BPF filter instructions, you generally just can't make use of it right away. exile.h provides an API inspired by pledge(), building on top of seccomp. It also provides an interface to manually restrict the system calls that can be issued.
|
||||||
|
|
||||||
|
Traditional methods employed to restrict file system access, like different uids/gids, chroot, bind-mounts, namespaces etc. may require administrator intervention, are perhaps only suitable
|
||||||
|
for daemons and not desktop applications, or are generally rather involved. As a positive example, Landlock since 5.13 is a vast improvement to limit file system access of processes. It also greatly simplifies exile.h' implementation of fs isolation.
|
||||||
|
|
||||||
|
Abstracting those details may help developers bring sandboxing into their applications.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
- Restricting file system access (using Landlock or Namespaces/chroot as fallback)
|
||||||
|
- Systemcall filtering (using seccomp-bpf). An interface inspired by OpenBSD's pledge() is available
|
||||||
|
- Dropping privileges in general, such as capabilities
|
||||||
|
- Isolating the application from the network, etc. through Namespaces
|
||||||
|
- Helpers to isolate single functions
|
||||||
|
|
||||||
- Systemcall filtering (using seccomp-bpf)
|
|
||||||
- restricting file system access (using Landlock and/or Namespaces)
|
## What it's not
|
||||||
- dropping privileges
|
A way for end users/administrators to restrict processes. In the future, a wrapper binary may be available to achieve this, but it generally aims for developers to bring sandboxing/isolation into their software. This allows a more fine-grained approach, as the developers are more familiar with their software. Applying restrictions with solutions like AppArmor requires
|
||||||
- isolating the application from the network, etc.
|
them to be present and installed on the system and it's easy to break things this way.
|
||||||
|
|
||||||
|
Therefore, software should ideally be written with sandboxing in mind from the beginning.
|
||||||
|
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
Will be available once the interface stabilizes.
|
||||||
|
|
||||||
|
It's recommended to start with [README.usage.md] to get a feeling for exile.h.
|
||||||
|
API-Documentation: [README.api.md]
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
TODO:
|
||||||
|
- seccomp must be kept up to date syscalls kernel
|
||||||
|
- ioctl does not know the fd, so checking values is kind of strange
|
||||||
|
- redundancies: some things are handled by capabilties, other by seccomp or both
|
||||||
|
- seccomp no deep argument inspection
|
||||||
|
- landlock: stat() does not apply
|
||||||
|
- no magic, be reasonable, devs should not get sloppy, restrict IPC.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Kernel >=3.17
|
Kernel >=3.17
|
||||||
|
|
||||||
``sys/capabilities.h`` header. Depending on your distribution, libcap
|
While mostly transparent to users of this API, kernel >= 5.13 is required to take advantage of Landlock. Furthermore, it depends on distro-provided kernels being reasonable and enabling it by default. In practise, Landlock maybe won't be used in some cases so exile.h will use a combination of namespaces, bind mounts and chroot as fallbacks.
|
||||||
might be needed for this.
|
|
||||||
|
|
||||||
While mostly transparent to users of this API, kernel >= 5.13 is required to take advantage of Landlock.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
|
|
||||||
### Does the process need to be priviliged to utilize the library?
|
### Does the process need to be privileged to utilize the library?
|
||||||
|
|
||||||
No.
|
No.
|
||||||
|
|
||||||
### It doesn't work on Debian!
|
### It doesn't work on my Debian version!
|
||||||
|
You can thank a Debian-specific kernel patch for that. Execute
|
||||||
You can thank a Debian-specific kernel patch for that. In the future,
|
|
||||||
the library may check against that. Execute
|
|
||||||
`echo 1 > /proc/sys/kernel/unprivileged_userns_clone` to disable that patch for now.
|
`echo 1 > /proc/sys/kernel/unprivileged_userns_clone` to disable that patch for now.
|
||||||
|
|
||||||
### Examples
|
Note that newer releases should not cause this problem any longer, as [explained](https://www.debian.org/releases/bullseye/amd64/release-notes/ch-information.en.html#linux-user-namespaces) in the Debian release notes.
|
||||||
- looqs: https://gitea.quitesimple.org/crtxcr/looqs
|
|
||||||
|
### Real-world usage
|
||||||
|
- looqs: https://github.com/quitesimpleorg/looqs
|
||||||
- qswiki: https://gitea.quitesimple.org/crtxcr/qswiki
|
- qswiki: https://gitea.quitesimple.org/crtxcr/qswiki
|
||||||
|
|
||||||
|
Outdated:
|
||||||
- cgit sandboxed: https://gitea.quitesimple.org/crtxcr/cgitsb
|
- cgit sandboxed: https://gitea.quitesimple.org/crtxcr/cgitsb
|
||||||
- qpdfviewsb sandboxed (quick and dirty): https://gitea.quitesimple.org/crtxcr/qpdfviewsb
|
- qpdfviewsb sandboxed (quick and dirty): https://gitea.quitesimple.org/crtxcr/qpdfviewsb
|
||||||
|
|
||||||
|
### Other projects
|
||||||
|
- [sandbox2](https://developers.google.com/code-sandboxing/sandbox2/)
|
||||||
|
|
||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
Contributions are very welcome. Options:
|
Contributions are very welcome. Options:
|
||||||
|
|
||||||
1. Pull-Request on [github](https://github.com/quitesimpleorg/qssb.h)
|
1. Pull-Request on [github](https://github.com/quitesimpleorg/exile.h)
|
||||||
2. Mail to `qssb at quitesimple.org` with instructions on where to pull the changes from.
|
2. Mail to `exile at quitesimple.org` with instructions on where to pull the changes from.
|
||||||
3. Mailing a classic patch/diff to the same address.
|
3. Mailing a classic patch/diff to the same address.
|
||||||
|
|
||||||
|
|
||||||
|
543
exile.h
Normal file
543
exile.h
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 Albert Schwarzkopf <mail at quitesimple dot org>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EXILE_H
|
||||||
|
#define EXILE_H
|
||||||
|
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/random.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/audit.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
|
#define capget(hdrp,datap) syscall(__NR_capget,hdrp,datap)
|
||||||
|
#define capset(hdrp,datap) syscall(__NR_capset,hdrp,datap)
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef HAVE_LANDLOCK
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,13,0)
|
||||||
|
/* TODO: Hopefully a fair assumption. But we need to runtime checks */
|
||||||
|
#define HAVE_LANDLOCK 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if HAVE_LANDLOCK == 1
|
||||||
|
#include <linux/landlock.h>
|
||||||
|
#if LANDLOCK_CREATE_RULESET_VERSION != (1U << 0)
|
||||||
|
#error "This landlock ABI version is not supported by exile.h (yet)"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_X86_64
|
||||||
|
#else
|
||||||
|
#error Seccomp support has not been tested for exile.h for this platform yet
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define EXILE_UNSHARE_NETWORK 1<<1
|
||||||
|
#define EXILE_UNSHARE_USER 1<<2
|
||||||
|
#define EXILE_UNSHARE_MOUNT 1<<3
|
||||||
|
#define EXILE_UNSHARE_AUTOMATIC 1<<4
|
||||||
|
|
||||||
|
#ifndef EXILE_LOG_ERROR
|
||||||
|
#define EXILE_LOG_ERROR(...) do { fprintf(stderr, "exile.h: %s(): Error: ", __func__); fprintf(stderr, __VA_ARGS__); } while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EXILE_TEMP_DIR
|
||||||
|
#define EXILE_TEMP_DIR "/tmp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define EXILE_SYS(x) __NR_##x
|
||||||
|
|
||||||
|
/* Allow all read-effect operations on the path */
|
||||||
|
#define EXILE_FS_ALLOW_ALL_READ 1<<0
|
||||||
|
/* Allow all write-effect operations on the path, such as normal writes, creation/deletion of files */
|
||||||
|
#define EXILE_FS_ALLOW_ALL_WRITE (1<<1)
|
||||||
|
#define EXILE_FS_ALLOW_EXEC 1<<2
|
||||||
|
#define EXILE_FS_ALLOW_DEV 1<<3
|
||||||
|
#define EXILE_FS_ALLOW_SETUID 1<<4
|
||||||
|
|
||||||
|
//don't mount recursive
|
||||||
|
#define EXILE_MOUNT_NOT_REC 1<<5
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Fine-granular approach available with landlock */
|
||||||
|
#if HAVE_LANDLOCK == 1
|
||||||
|
#define EXILE_FS_ALLOW_REMOVE_DIR (1 << 7)
|
||||||
|
#define EXILE_FS_ALLOW_REMOVE_FILE (1 << 8)
|
||||||
|
#define EXILE_FS_ALLOW_MAKE_CHAR (1 << 9)
|
||||||
|
#define EXILE_FS_ALLOW_MAKE_DIR (1 << 10)
|
||||||
|
#define EXILE_FS_ALLOW_MAKE_REG (1 << 11)
|
||||||
|
#define EXILE_FS_ALLOW_MAKE_SOCK (1 << 12)
|
||||||
|
#define EXILE_FS_ALLOW_MAKE_FIFO (1 << 13)
|
||||||
|
#define EXILE_FS_ALLOW_MAKE_BLOCK (1 << 14)
|
||||||
|
#define EXILE_FS_ALLOW_MAKE_SYM (1 << 15)
|
||||||
|
#define EXILE_FS_ALLOW_WRITE_FILE (1 << 16)
|
||||||
|
#define EXILE_FS_ALLOW_READ_DIR (1 << 17)
|
||||||
|
#define EXILE_FS_ALLOW_REMOVE (1 << 18)
|
||||||
|
|
||||||
|
#ifndef landlock_create_ruleset
|
||||||
|
static inline int landlock_create_ruleset(
|
||||||
|
const struct landlock_ruleset_attr *const attr,
|
||||||
|
const size_t size, const __u32 flags)
|
||||||
|
{
|
||||||
|
return syscall(__NR_landlock_create_ruleset, attr, size, flags);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef landlock_add_rule
|
||||||
|
static inline int landlock_add_rule(const int ruleset_fd,
|
||||||
|
const enum landlock_rule_type rule_type,
|
||||||
|
const void *const rule_attr, const __u32 flags)
|
||||||
|
{
|
||||||
|
return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type,
|
||||||
|
rule_attr, flags);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef landlock_restrict_self
|
||||||
|
static inline int landlock_restrict_self(const int ruleset_fd,
|
||||||
|
const __u32 flags)
|
||||||
|
{
|
||||||
|
return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
#ifndef __NR_pkey_mprotect
|
||||||
|
#define __NR_pkey_mprotect 329
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_pkey_alloc
|
||||||
|
#define __NR_pkey_alloc 330
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_pkey_free
|
||||||
|
#define __NR_pkey_free 331
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_statx
|
||||||
|
#define __NR_statx 332
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_io_pgetevents
|
||||||
|
#define __NR_io_pgetevents 333
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_rseq
|
||||||
|
#define __NR_rseq 334
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_pidfd_send_signal
|
||||||
|
#define __NR_pidfd_send_signal 424
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_io_uring_setup
|
||||||
|
#define __NR_io_uring_setup 425
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_io_uring_enter
|
||||||
|
#define __NR_io_uring_enter 426
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_io_uring_register
|
||||||
|
#define __NR_io_uring_register 427
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_open_tree
|
||||||
|
#define __NR_open_tree 428
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_move_mount
|
||||||
|
#define __NR_move_mount 429
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_fsopen
|
||||||
|
#define __NR_fsopen 430
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_fsconfig
|
||||||
|
#define __NR_fsconfig 431
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_fsmount
|
||||||
|
#define __NR_fsmount 432
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_fspick
|
||||||
|
#define __NR_fspick 433
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_pidfd_open
|
||||||
|
#define __NR_pidfd_open 434
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_clone3
|
||||||
|
#define __NR_clone3 435
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_futex_waitv
|
||||||
|
#define __NR_futex_waitv 449
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_close_range
|
||||||
|
#define __NR_close_range 436
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_openat2
|
||||||
|
#define __NR_openat2 437
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_pidfd_getfd
|
||||||
|
#define __NR_pidfd_getfd 438
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_faccessat2
|
||||||
|
#define __NR_faccessat2 439
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_process_madvise
|
||||||
|
#define __NR_process_madvise 440
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_epoll_pwait2
|
||||||
|
#define __NR_epoll_pwait2 441
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_mount_setattr
|
||||||
|
#define __NR_mount_setattr 442
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_quotactl_fd
|
||||||
|
#define __NR_quotactl_fd 443
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_landlock_create_ruleset
|
||||||
|
#define __NR_landlock_create_ruleset 444
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_landlock_add_rule
|
||||||
|
#define __NR_landlock_add_rule 445
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_landlock_restrict_self
|
||||||
|
#define __NR_landlock_restrict_self 446
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_memfd_secret
|
||||||
|
#define __NR_memfd_secret 447
|
||||||
|
#endif
|
||||||
|
#ifndef __NR_process_mrelease
|
||||||
|
#define __NR_process_mrelease 448
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct syscall_vow_map
|
||||||
|
{
|
||||||
|
long syscall;
|
||||||
|
uint64_t vowmask;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct str_to_vow_map
|
||||||
|
{
|
||||||
|
const char *str;
|
||||||
|
uint64_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct exile_path_policy
|
||||||
|
{
|
||||||
|
const char *path;
|
||||||
|
unsigned int policy;
|
||||||
|
struct exile_path_policy *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Special values */
|
||||||
|
#define EXILE_SYSCALL_MATCH_ALL -1
|
||||||
|
/* exit the bpf filter, not matching policy. Go to the next syscall (or the default action, if none left to check) */
|
||||||
|
#define EXILE_SYSCALL_EXIT_BPF_NO_MATCH 255
|
||||||
|
/* exit the bpf filter, go directly to the action for the syscall (skip all other args checks) */
|
||||||
|
#define EXILE_SYSCALL_EXIT_BPF_RETURN 254
|
||||||
|
|
||||||
|
#define EXILE_SYSCALL_ALLOW 1
|
||||||
|
#define EXILE_SYSCALL_DENY_KILL_PROCESS 2
|
||||||
|
#define EXILE_SYSCALL_DENY_RET_ERROR 3
|
||||||
|
#define EXILE_SYSCALL_DENY_RET_NOSYS 4
|
||||||
|
|
||||||
|
#define EXILE_BPF_NOP \
|
||||||
|
BPF_STMT(BPF_JMP+BPF_JA,0)
|
||||||
|
|
||||||
|
/* A few more dirty markers to simplify array block initializers. We replace those
|
||||||
|
in append_syscall_to_bpf(). The k value is meaningless here and we don't expect
|
||||||
|
to ever have filter code actually wanting to jump that many steps forward. So
|
||||||
|
they serve as an special value we will replace with actual ones. */
|
||||||
|
#define EXILE_BPF_RETURN_MATCHING \
|
||||||
|
BPF_STMT(BPF_JMP+BPF_JA,1234)
|
||||||
|
|
||||||
|
#define EXILE_BPF_RETURN_NOT_MATCHING \
|
||||||
|
BPF_STMT(BPF_JMP+BPF_JA,5678)
|
||||||
|
|
||||||
|
#define EXILE_BPF_LOAD_SECCOMP_ARG(nr) \
|
||||||
|
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, args[nr])))
|
||||||
|
|
||||||
|
#define EXILE_BPF_CMP_EQ(val,t,f) \
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, val, t, f)
|
||||||
|
|
||||||
|
#define EXILE_BPF_CMP_SET(val,t,f) \
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, val, t, f)
|
||||||
|
|
||||||
|
/* Immediatly go to the syscall action, do not process any other arg filters */
|
||||||
|
#define EXILE_BPF_MATCH(argval) \
|
||||||
|
EXILE_BPF_CMP_EQ(argval, EXILE_SYSCALL_EXIT_BPF_RETURN, 0)
|
||||||
|
|
||||||
|
#define EXILE_BPF_MATCH_SET(argval) \
|
||||||
|
EXILE_BPF_CMP_SET(argval, EXILE_SYSCALL_EXIT_BPF_RETURN, 0)
|
||||||
|
|
||||||
|
/* Immediatly go beyond the syscall action, do not process any other arg filters. What to do with this syscall
|
||||||
|
is thus up to the default policy */
|
||||||
|
#define EXILE_BPF_NO_MATCH(argval) \
|
||||||
|
EXILE_BPF_CMP_EQ(argval, EXILE_SYSCALL_EXIT_BPF_NO_MATCH, 0)
|
||||||
|
|
||||||
|
#define EXILE_BPF_NO_MATCH_SET(argval) \
|
||||||
|
EXILE_BPF_CMP_SET(argval, EXILE_SYSCALL_EXIT_BPF_NO_MATCH, 0)
|
||||||
|
|
||||||
|
|
||||||
|
/* Pledge definitions */
|
||||||
|
#define EXILE_SYSCALL_VOW_CHOWN ((uint64_t)1<<1)
|
||||||
|
#define EXILE_SYSCALL_VOW_CLONE ((uint64_t)1<<2)
|
||||||
|
#define EXILE_SYSCALL_VOW_CPATH ((uint64_t)1<<3)
|
||||||
|
#define EXILE_SYSCALL_VOW_DPATH ((uint64_t)1<<4)
|
||||||
|
#define EXILE_SYSCALL_VOW_EXEC ((uint64_t)1<<5)
|
||||||
|
#define EXILE_SYSCALL_VOW_FATTR ((uint64_t)1<<6)
|
||||||
|
#define EXILE_SYSCALL_VOW_FSNOTIFY ((uint64_t)1<<7)
|
||||||
|
#define EXILE_SYSCALL_VOW_ID ((uint64_t)1<<8)
|
||||||
|
#define EXILE_SYSCALL_VOW_INET ((uint64_t)1<<9)
|
||||||
|
#define EXILE_SYSCALL_VOW_IOCTL ((uint64_t)1<<10)
|
||||||
|
#define EXILE_SYSCALL_VOW_PRCTL ((uint64_t)1<<11)
|
||||||
|
#define EXILE_SYSCALL_VOW_PROC ((uint64_t)1<<12)
|
||||||
|
#define EXILE_SYSCALL_VOW_PROT_EXEC ((uint64_t)1<<13)
|
||||||
|
#define EXILE_SYSCALL_VOW_RPATH ((uint64_t)1<<14)
|
||||||
|
#define EXILE_SYSCALL_VOW_SCHED ((uint64_t)1<<15)
|
||||||
|
#define EXILE_SYSCALL_VOW_SECCOMP_INSTALL ((uint64_t)1<<16)
|
||||||
|
#define EXILE_SYSCALL_VOW_SHM ((uint64_t)1<<17)
|
||||||
|
#define EXILE_SYSCALL_VOW_STDIO ((uint64_t)1<<18)
|
||||||
|
#define EXILE_SYSCALL_VOW_THREAD ((uint64_t)1<<19)
|
||||||
|
#define EXILE_SYSCALL_VOW_UNIX ((uint64_t)1<<20)
|
||||||
|
#define EXILE_SYSCALL_VOW_WPATH ((uint64_t)1<<21)
|
||||||
|
|
||||||
|
#define EXILE_SYSCALL_VOW_DENY_ERROR ((uint64_t)1<<63)
|
||||||
|
|
||||||
|
|
||||||
|
#define EXILE_ARGFILTERS_COUNT 60
|
||||||
|
|
||||||
|
|
||||||
|
#define EXILE_FLAG_ADD_PATH_POLICY_FAIL (1u<<1)
|
||||||
|
#define EXILE_FLAG_ADD_SYSCALL_POLICY_FAIL (1u<<2)
|
||||||
|
|
||||||
|
struct exile_syscall_policy
|
||||||
|
{
|
||||||
|
struct sock_filter argfilters[EXILE_ARGFILTERS_COUNT];
|
||||||
|
size_t argfilterscount;
|
||||||
|
long syscall;
|
||||||
|
unsigned int policy;
|
||||||
|
struct exile_syscall_policy *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Policy tells exile what to do */
|
||||||
|
struct exile_policy
|
||||||
|
{
|
||||||
|
int drop_caps;
|
||||||
|
int preserve_cwd;
|
||||||
|
int not_dumpable;
|
||||||
|
int no_new_privs;
|
||||||
|
int no_fs;
|
||||||
|
int no_new_fds;
|
||||||
|
int keep_fds_open;
|
||||||
|
int namespace_options;
|
||||||
|
int disable_syscall_filter;
|
||||||
|
/* Bind mounts all paths in path_policies into the chroot and applies
|
||||||
|
non-landlock policies */
|
||||||
|
int mount_path_policies_to_chroot;
|
||||||
|
char chroot_target_path[PATH_MAX];
|
||||||
|
const char *chdir_path;
|
||||||
|
|
||||||
|
uint64_t vow_promises;
|
||||||
|
|
||||||
|
/* Do not manually add policies here, use exile_append_path_policies() */
|
||||||
|
struct exile_path_policy *path_policies;
|
||||||
|
struct exile_path_policy **path_policies_tail;
|
||||||
|
|
||||||
|
/* Do not manually add policies here, use exile_append_syscall_policy() */
|
||||||
|
struct exile_syscall_policy *syscall_policies;
|
||||||
|
struct exile_syscall_policy **syscall_policies_tail;
|
||||||
|
|
||||||
|
uint32_t exile_flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Converts the whitespace separated vows strings to vows flags
|
||||||
|
*
|
||||||
|
* This mainly helps readability, as lots of flags ORed together is not
|
||||||
|
* very readable.
|
||||||
|
*
|
||||||
|
* If an unkown string is found, abort() is called.
|
||||||
|
*/
|
||||||
|
uint64_t exile_vows_from_str(const char *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we can use landlock, return 1, otherwise 0
|
||||||
|
*/
|
||||||
|
int exile_landlock_is_available();
|
||||||
|
|
||||||
|
int exile_append_syscall_policy(struct exile_policy *exile_policy, long syscall, unsigned int syscall_policy, struct sock_filter *argfilters, size_t n);
|
||||||
|
|
||||||
|
int exile_append_syscall_default_policy(struct exile_policy *exile_policy, unsigned int default_policy);
|
||||||
|
|
||||||
|
struct exile_syscall_filter
|
||||||
|
{
|
||||||
|
uint64_t vowmask; /* Apply filter if this mask is set. 0 = ignore mask, apply always */
|
||||||
|
struct sock_filter filter;
|
||||||
|
int whenset; /* 1 = Filter should be added if vowmask is contained in pledge mask, otherwise won't be added. */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define COUNT_EXILE_SYSCALL_FILTER(f) \
|
||||||
|
sizeof(f)/sizeof(f[0])
|
||||||
|
|
||||||
|
#define EXILE_SYSCALL_FILTER_LOAD_ARG(val) \
|
||||||
|
{ 0, EXILE_BPF_LOAD_SECCOMP_ARG(val), 0}
|
||||||
|
|
||||||
|
/* Returns, for the specific syscall, the correct sock_filter struct for the provided vow_promises
|
||||||
|
*
|
||||||
|
* Returns: 0 if none copied, otherwise the number of entries in "filter".
|
||||||
|
*/
|
||||||
|
int get_vow_argfilter(long syscall, uint64_t vow_promises, struct sock_filter *filter , int *policy);
|
||||||
|
|
||||||
|
|
||||||
|
int exile_append_vow_promises(struct exile_policy *policy, uint64_t vow_promises);
|
||||||
|
|
||||||
|
|
||||||
|
/* Creates an empty policy struct without opinionated defaults.
|
||||||
|
*
|
||||||
|
* Must be freed using exile_free_policy()
|
||||||
|
* @returns: empty policy
|
||||||
|
*/
|
||||||
|
struct exile_policy *exile_create_policy();
|
||||||
|
|
||||||
|
|
||||||
|
/* Creates the default policy
|
||||||
|
* Must be freed using exile_free_policy()
|
||||||
|
*
|
||||||
|
* @returns: default policy
|
||||||
|
*/
|
||||||
|
struct exile_policy *exile_init_policy();
|
||||||
|
|
||||||
|
|
||||||
|
/* Appends path policies to the exile_policy object
|
||||||
|
* The last paramater must be NULL
|
||||||
|
*
|
||||||
|
* This function does not copy parameters. All passed paths
|
||||||
|
* MUST NOT be freed until exile_enable_policy() is called!
|
||||||
|
*
|
||||||
|
* @returns: 0 on success, -1 on failure */
|
||||||
|
int exile_append_path_policies(struct exile_policy *exile_policy, unsigned int path_policy, ...);
|
||||||
|
#define exile_append_path_policies(e, p, ...) exile_append_path_policies(e, p, __VA_ARGS__, NULL)
|
||||||
|
|
||||||
|
int path_policy_needs_landlock(struct exile_path_policy *path_policy);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Frees the memory taken by a exile_policy object
|
||||||
|
*/
|
||||||
|
void exile_free_policy(struct exile_policy *ctxt);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enables the seccomp policy
|
||||||
|
*
|
||||||
|
* policy: exile policy object
|
||||||
|
*
|
||||||
|
* @returns: 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
int exile_enable_syscall_policy(struct exile_policy *policy);
|
||||||
|
|
||||||
|
|
||||||
|
int exile_enable_policy(struct exile_policy *policy);
|
||||||
|
|
||||||
|
|
||||||
|
/* Convenience wrapper for the vow-related subset of exile.h
|
||||||
|
*
|
||||||
|
* Only installs seccomp filters for the specified vow promises.
|
||||||
|
*
|
||||||
|
* Useful if only vow is required from exile.h, but nothing else
|
||||||
|
*
|
||||||
|
* Comparable with OpenBSD's pledge(), subsequent calls can only reduce allowed syscalls.
|
||||||
|
*
|
||||||
|
* Here, adding more promises than a previous call set may return success, but
|
||||||
|
* won't be allowed during execution.
|
||||||
|
*
|
||||||
|
* Due to the nature of seccomp, it's furthermore required the EXILE_SYSCALL_VOW_SECCOMP_INSTALL promise
|
||||||
|
* is set if further calls are expected. Generally, it's reasonable for the last call to
|
||||||
|
* exile_vow() a program makes to not set EXILE_SYSCALL_VOW_SECCOMP_INSTALL.
|
||||||
|
*
|
||||||
|
* There are no seperate exec_promises. All children of the process inherit the filter.
|
||||||
|
* .
|
||||||
|
* Return value: 0 on success, any other value on failure.
|
||||||
|
*/
|
||||||
|
int exile_vow(uint64_t promises);
|
||||||
|
|
||||||
|
struct exile_launch_params
|
||||||
|
{
|
||||||
|
struct exile_policy *policy; /* Policy to activate before jumping to func */
|
||||||
|
int (*func)(void *); /* Function to be sandboxed */
|
||||||
|
void *funcarg; /* Arg to be passed */
|
||||||
|
int child_read_pipe[2];
|
||||||
|
int child_write_pipe[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct exile_launch_result
|
||||||
|
{
|
||||||
|
int tid;
|
||||||
|
int read_fd;
|
||||||
|
int write_fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
int exile_clone_handle(void *arg);
|
||||||
|
/* Helper to easily execute a single function sandboxed.
|
||||||
|
*
|
||||||
|
* Creates a child-process, then activates the policy contained in launch_params,
|
||||||
|
* and jumps to the specified function, passing the specified argument to it.
|
||||||
|
* Returns a fd connected to stdout in the child process, as well as a fd allowing to write
|
||||||
|
* to the child.
|
||||||
|
*
|
||||||
|
* if cloneflags is 0, the default ones are passed to clone(), otherwise the value of cloneflags
|
||||||
|
*
|
||||||
|
* Return value: Negative on error, otherwise the file descriptor to read from*/
|
||||||
|
int exile_launch(struct exile_launch_params *launch_params, struct exile_launch_result *launch_result);
|
||||||
|
|
||||||
|
|
||||||
|
/* Helper for exile_launch, to easily read all output from a function
|
||||||
|
* This function will read all output from a sandboxed function. It's up to the caller to ensure
|
||||||
|
* that enough memory will be available.
|
||||||
|
*
|
||||||
|
* The result is \0 terminated. The "n" parameter contains the size of the result, not including the \0.
|
||||||
|
*
|
||||||
|
* Return value: All data written by the function. The result should be passed to free() once not needed. NULL will
|
||||||
|
* be returned on error.
|
||||||
|
*/
|
||||||
|
char *exile_launch_get(struct exile_launch_params *launch_params, size_t *n);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
201
exile.hpp
Normal file
201
exile.hpp
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
#include "exile.h"
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <memory>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#ifndef EXILE_MMAP_SIZE
|
||||||
|
#define EXILE_MMAP_SIZE 128 * 1024 * 1024 //128MB
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T, typename U, typename ... Args>
|
||||||
|
class launch_arg
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_copyable_v<T>);
|
||||||
|
static_assert(!std::is_pointer_v<T>);
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct exile_policy *policy;
|
||||||
|
T *result_shm;
|
||||||
|
U fn;
|
||||||
|
std::tuple<Args...> args;
|
||||||
|
|
||||||
|
launch_arg(struct exile_policy *policy, T *result_shm, U fn, Args && ... args) : policy(policy),
|
||||||
|
result_shm(result_shm), fn(fn), args(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename U, typename ... Args>
|
||||||
|
class launch_arg_serializer
|
||||||
|
{
|
||||||
|
static_assert(std::is_copy_constructible_v<T>);
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct exile_policy *policy;
|
||||||
|
char *serialize_buffer;
|
||||||
|
size_t n;
|
||||||
|
U fn;
|
||||||
|
std::tuple<Args...> args;
|
||||||
|
|
||||||
|
const std::function<size_t (const T &, char *, size_t n)> &serializer;
|
||||||
|
const std::function<T(const char * buf, size_t n)> &deserializer;
|
||||||
|
|
||||||
|
launch_arg_serializer(struct exile_policy *policy, char *serialize_buffer, size_t n, const std::function<size_t (const T &, char *, size_t)> &serializer, const std::function<T(const char *, size_t)> &deserializer, U fn, Args && ... args) : policy(policy), serialize_buffer(serialize_buffer), n(n), fn(fn), args(std::forward<Args>(args)...), serializer(serializer), deserializer(deserializer) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename U, typename ... Args>
|
||||||
|
int exile_clone_handle_trivial(void * arg)
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_copyable_v<T>);
|
||||||
|
static_assert(!std::is_pointer_v<T>);
|
||||||
|
|
||||||
|
launch_arg<T, U, Args...> *launchargs = (launch_arg<T, U, Args...> *) arg;
|
||||||
|
int ret = exile_enable_policy(launchargs->policy);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
EXILE_LOG_ERROR("exile_enable_policy() failed: %s\n", strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
T result = std::apply(launchargs->fn, launchargs->args);
|
||||||
|
std::cout << result;
|
||||||
|
memcpy(launchargs->result_shm, &result, sizeof(T));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename U, typename ... Args>
|
||||||
|
int exile_clone_handle_serializer(void * arg)
|
||||||
|
{
|
||||||
|
static_assert(std::is_copy_constructible_v<T>);
|
||||||
|
|
||||||
|
launch_arg_serializer<T, U, Args...> *launchargs = (launch_arg_serializer<T, U, Args...> *) arg;
|
||||||
|
int ret = exile_enable_policy(launchargs->policy);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
EXILE_LOG_ERROR("exile_enable_policy() failed: %s\n", strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
T result = std::apply(launchargs->fn, launchargs->args);
|
||||||
|
/* TODO: exception handling */
|
||||||
|
/* TODO: ugly :S */
|
||||||
|
char *target = launchargs->serialize_buffer + sizeof(size_t);
|
||||||
|
size_t n = launchargs->n - sizeof(size_t);
|
||||||
|
|
||||||
|
size_t size = launchargs->serializer(result, target, n);
|
||||||
|
memcpy(launchargs->serialize_buffer, &size, sizeof(size_t));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int do_clone(int (*clonefn)(void *), void *launcharg)
|
||||||
|
{
|
||||||
|
struct rlimit rlimit;
|
||||||
|
int ret = getrlimit(RLIMIT_STACK, &rlimit);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
EXILE_LOG_ERROR("Failed to get stack size: %s\n", strerror(errno));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
size_t size = rlimit.rlim_cur;
|
||||||
|
char *stack = (char *) calloc(1, size);
|
||||||
|
char *stackbegin = stack;
|
||||||
|
if(stack == NULL)
|
||||||
|
{
|
||||||
|
EXILE_LOG_ERROR("Failed to allocate stack memory for child\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
stack += size;
|
||||||
|
|
||||||
|
ret = clone(clonefn, stack, 17 /* SIGCHLD */, launcharg);
|
||||||
|
int status = 0;
|
||||||
|
waitpid(ret, &status, __WALL);
|
||||||
|
free(stackbegin);
|
||||||
|
if(WIFEXITED(status))
|
||||||
|
{
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
/* TODO: exception or what? */
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename U, typename ... Args>
|
||||||
|
typename std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, T> exile_launch(struct exile_policy *policy, U fn, Args && ... args)
|
||||||
|
{
|
||||||
|
size_t mapsize = sizeof(T);
|
||||||
|
T * sharedbuf = (T *) mmap(NULL, mapsize , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
|
||||||
|
if(sharedbuf == NULL)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("mmap failed: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<void> deleter(nullptr, [sharedbuf, mapsize](...){ munmap(sharedbuf, mapsize); });
|
||||||
|
launch_arg<T, U, Args...> launcharg(policy, sharedbuf, fn, std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
int (*clonefn)(void *) = &exile_clone_handle_trivial<T, U, Args...>;
|
||||||
|
/* TODO: exception or what? */
|
||||||
|
int ret = do_clone(clonefn, &launcharg);
|
||||||
|
if(ret == 0)
|
||||||
|
{
|
||||||
|
return *sharedbuf;
|
||||||
|
}
|
||||||
|
throw std::runtime_error(std::string("clone() failed: " + std::to_string(ret)));
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T, typename U, typename ... Args>
|
||||||
|
typename std::enable_if_t<std::is_pointer_v<T> || (!std::is_trivially_copyable_v<T> && std::is_copy_constructible_v<T>), T>
|
||||||
|
exile_launch(struct exile_policy *policy, const std::function<size_t (const T &, char *, size_t)> &serializer, const std::function<T(const char *, size_t)> &deserializer, U fn, Args && ... args)
|
||||||
|
{
|
||||||
|
size_t mapsize = EXILE_MMAP_SIZE;
|
||||||
|
char *sharedbuf = (char *) mmap(NULL, mapsize , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
|
||||||
|
if(sharedbuf == NULL)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("mmap failed: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
std::shared_ptr<void> deleter(nullptr, [sharedbuf, mapsize](...){ munmap(sharedbuf, mapsize); });
|
||||||
|
|
||||||
|
|
||||||
|
launch_arg_serializer<T, U, Args...> launcharg(policy, sharedbuf, mapsize, serializer, deserializer, fn, std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
int (*clonefn)(void *) = &exile_clone_handle_serializer<T, U, Args...>;
|
||||||
|
/* TODO: exception or what? */
|
||||||
|
int ret = do_clone(clonefn, &launcharg);
|
||||||
|
if(ret == 0)
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
memcpy(&size, sharedbuf, sizeof(size));
|
||||||
|
|
||||||
|
return deserializer(sharedbuf + sizeof(size_t), size);
|
||||||
|
}
|
||||||
|
throw std::runtime_error(std::string("clone() failed: " + std::to_string(ret)));
|
||||||
|
return T();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
std::basic_string<typename T::value_type> deserialize_stdstring(const char *buf, size_t n)
|
||||||
|
{
|
||||||
|
return std::basic_string<typename T::value_type> { buf, n };
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
size_t serialize_stdstring(const std::basic_string<typename T::value_type> &t, char *buf, size_t n)
|
||||||
|
{
|
||||||
|
if(n < t.size())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(buf, t.data(), t.size());
|
||||||
|
return t.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T, typename U, typename ... Args>
|
||||||
|
std::basic_string<typename T::value_type> exile_launch(struct exile_policy *policy, U fn, Args && ... args)
|
||||||
|
{
|
||||||
|
return exile_launch<T, U, Args...>(policy, &serialize_stdstring<T>, &deserialize_stdstring<T>, fn, std::forward<Args>(args) ...);
|
||||||
|
}
|
55
gengroup.py
55
gengroup.py
@ -1,55 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print("Usage: gengroup groupfile")
|
|
||||||
sys.exit(1)
|
|
||||||
fd = open(sys.argv[1], "r")
|
|
||||||
|
|
||||||
lines = fd.read().splitlines()
|
|
||||||
|
|
||||||
groupnames = set()
|
|
||||||
ifndef = dict()
|
|
||||||
|
|
||||||
def print_ifndefs():
|
|
||||||
for name in ifndef:
|
|
||||||
print("#ifndef __NR_%s" % name)
|
|
||||||
print("#define __NR_%s %s" % (name, ifndef[name]))
|
|
||||||
print("#endif")
|
|
||||||
|
|
||||||
def print_defines(names):
|
|
||||||
names = sorted(names)
|
|
||||||
i = 0
|
|
||||||
for name in names:
|
|
||||||
define = "#define %s ((uint64_t)1<<%s)" % (name, i)
|
|
||||||
print(define)
|
|
||||||
i = i + 1
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
if line[0] == '#':
|
|
||||||
continue
|
|
||||||
|
|
||||||
splitted = line.split(' ')
|
|
||||||
if len(splitted) < 2:
|
|
||||||
print("Misformated line:", line)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
currentsyscall = splitted[0]
|
|
||||||
currentgroups = splitted[1].split(',')
|
|
||||||
|
|
||||||
flags = splitted[2] if len(splitted) > 2 else ""
|
|
||||||
if any( not s or s.isspace() for s in currentgroups ):
|
|
||||||
print("Misformated line (empty values):", line)
|
|
||||||
sys.exit(1)
|
|
||||||
groupnames.update(currentgroups)
|
|
||||||
|
|
||||||
genifndef = re.match(r"genifndef\((\d+)*\)", flags)
|
|
||||||
if genifndef:
|
|
||||||
ifndef[currentsyscall] = genifndef.groups(1)[0]
|
|
||||||
|
|
||||||
array_line = "{QSSB_SYS(%s), %s}," % (currentsyscall, '|'.join(currentgroups))
|
|
||||||
print(array_line)
|
|
||||||
|
|
||||||
print_ifndefs()
|
|
||||||
print_defines(groupnames)
|
|
||||||
|
|
@ -1,363 +0,0 @@
|
|||||||
# Assign system calls to groups. In the future, may also include simple arg filtering.
|
|
||||||
read QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
write QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
open QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
close QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
stat QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
fstat QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
lstat QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
poll QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
lseek QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
mmap QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
mprotect QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
munmap QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
brk QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
rt_sigaction QSSB_SYSCGROUP_RT,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
rt_sigprocmask QSSB_SYSCGROUP_RT,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
rt_sigreturn QSSB_SYSCGROUP_RT,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
ioctl QSSB_SYSCGROUP_IOCTL,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
pread64 QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
pwrite64 QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
readv QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
writev QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
access QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
pipe QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
select QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
sched_yield QSSB_SYSCGROUP_SCHED,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
mremap QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
msync QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
mincore QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
madvise QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
shmget QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
shmat QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
shmctl QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
dup QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
dup2 QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
pause QSSB_SYSCGROUP_PAUSE,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
nanosleep QSSB_SYSCGROUP_TIMER,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
getitimer QSSB_SYSCGROUP_TIMER,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
alarm QSSB_SYSCGROUP_TIMER,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
setitimer QSSB_SYSCGROUP_TIMER,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
getpid QSSB_SYSCGROUP_PROCESS,QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
sendfile QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
socket QSSB_SYSCGROUP_SOCKET
|
|
||||||
connect QSSB_SYSCGROUP_SOCKET
|
|
||||||
accept QSSB_SYSCGROUP_SOCKET
|
|
||||||
sendto QSSB_SYSCGROUP_SOCKET
|
|
||||||
recvfrom QSSB_SYSCGROUP_SOCKET
|
|
||||||
sendmsg QSSB_SYSCGROUP_SOCKET
|
|
||||||
recvmsg QSSB_SYSCGROUP_SOCKET
|
|
||||||
shutdown QSSB_SYSCGROUP_SOCKET
|
|
||||||
bind QSSB_SYSCGROUP_SOCKET
|
|
||||||
listen QSSB_SYSCGROUP_SOCKET
|
|
||||||
getsockname QSSB_SYSCGROUP_SOCKET
|
|
||||||
getpeername QSSB_SYSCGROUP_SOCKET
|
|
||||||
socketpair QSSB_SYSCGROUP_SOCKET,QSSB_SYSCGROUP_IPC
|
|
||||||
setsockopt QSSB_SYSCGROUP_SOCKET
|
|
||||||
getsockopt QSSB_SYSCGROUP_SOCKET
|
|
||||||
clone QSSB_SYSCGROUP_CLONE,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
fork QSSB_SYSCGROUP_CLONE,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
vfork QSSB_SYSCGROUP_CLONE,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
execve QSSB_SYSCGROUP_CLONE,QSSB_SYSCGROUP_EXEC
|
|
||||||
exit QSSB_SYSCGROUP_PROCESS,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
wait4 QSSB_SYSCGROUP_EXEC
|
|
||||||
kill QSSB_SYSCGROUP_KILL
|
|
||||||
uname QSSB_SYSCGROUP_SYS,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
semget QSSB_SYSCGROUP_SHM,QSSB_SYSCGROUP_IPC,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
semop QSSB_SYSCGROUP_SHM,QSSB_SYSCGROUP_IPC,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
semctl QSSB_SYSCGROUP_SHM,QSSB_SYSCGROUP_IPC,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
shmdt QSSB_SYSCGROUP_SHM,QSSB_SYSCGROUP_IPC,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
msgget QSSB_SYSCGROUP_IPC,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
msgsnd QSSB_SYSCGROUP_IPC,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
msgrcv QSSB_SYSCGROUP_IPC,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
msgctl QSSB_SYSCGROUP_IPC,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
fcntl QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
flock QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
fsync QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
fdatasync QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
truncate QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
ftruncate QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
getdents QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
getcwd QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
chdir QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
fchdir QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
rename QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
mkdir QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
rmdir QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
creat QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
link QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
unlink QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
symlink QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
readlink QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
chmod QSSB_SYSCGROUP_PERMS,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
fchmod QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
chown QSSB_SYSCGROUP_PERMS,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
fchown QSSB_SYSCGROUP_PERMS,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
lchown QSSB_SYSCGROUP_PERMS,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
umask QSSB_SYSCGROUP_PERMS,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
gettimeofday QSSB_SYSCGROUP_TIME,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
getrlimit QSSB_SYSCGROUP_RES,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
getrusage QSSB_SYSCGROUP_RES,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
sysinfo QSSB_SYSCGROUP_SYS,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
times QSSB_SYSCGROUP_TIME,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
ptrace QSSB_SYSCGROUP_PTRACE,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
getuid QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
syslog QSSB_SYSCGROUP_SYS
|
|
||||||
getgid QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
setuid QSSB_SYSCGROUP_ID
|
|
||||||
setgid QSSB_SYSCGROUP_ID
|
|
||||||
geteuid QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
getegid QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
setpgid QSSB_SYSCGROUP_ID
|
|
||||||
getppid QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
getpgrp QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
setsid QSSB_SYSCGROUP_ID
|
|
||||||
setreuid QSSB_SYSCGROUP_ID
|
|
||||||
setregid QSSB_SYSCGROUP_ID
|
|
||||||
getgroups QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
setgroups QSSB_SYSCGROUP_ID
|
|
||||||
setresuid QSSB_SYSCGROUP_ID
|
|
||||||
getresuid QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
setresgid QSSB_SYSCGROUP_ID
|
|
||||||
getresgid QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
getpgid QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
setfsuid QSSB_SYSCGROUP_ID
|
|
||||||
setfsgid QSSB_SYSCGROUP_ID
|
|
||||||
getsid QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
capget QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
capset QSSB_SYSCGROUP_ID
|
|
||||||
rt_sigpending QSSB_SYSCGROUP_RT,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
rt_sigtimedwait QSSB_SYSCGROUP_RT,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
rt_sigqueueinfo QSSB_SYSCGROUP_RT,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
rt_sigsuspend QSSB_SYSCGROUP_RT,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
sigaltstack QSSB_SYSCGROUP_THREAD,QSSB_SYSCGROUP_SIGNAL
|
|
||||||
utime QSSB_SYSCGROUP_TIME,QSSB_SYSCGROUP_FS
|
|
||||||
mknod QSSB_SYSCGROUP_DEV,QSSB_SYSCGROUP_FS
|
|
||||||
uselib QSSB_SYSCGROUP_LIB,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
personality QSSB_SYSCGROUP_PROCESS
|
|
||||||
ustat QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_STAT,QSSB_SYSCGROUP_FS
|
|
||||||
statfs QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_STAT,QSSB_SYSCGROUP_FS
|
|
||||||
fstatfs QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_STAT,QSSB_SYSCGROUP_FS
|
|
||||||
sysfs QSSB_SYSCGROUP_SYS,QSSB_SYSCGROUP_FS
|
|
||||||
getpriority QSSB_SYSCGROUP_SCHED
|
|
||||||
setpriority QSSB_SYSCGROUP_SCHED
|
|
||||||
sched_setparam QSSB_SYSCGROUP_SCHED
|
|
||||||
sched_getparam QSSB_SYSCGROUP_SCHED
|
|
||||||
sched_setscheduler QSSB_SYSCGROUP_SCHED
|
|
||||||
sched_getscheduler QSSB_SYSCGROUP_SCHED
|
|
||||||
sched_get_priority_max QSSB_SYSCGROUP_SCHED
|
|
||||||
sched_get_priority_min QSSB_SYSCGROUP_SCHED
|
|
||||||
sched_rr_get_interval QSSB_SYSCGROUP_SCHED
|
|
||||||
mlock QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
munlock QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
mlockall QSSB_SYSCGROUP_MEMORY
|
|
||||||
munlockall QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
vhangup QSSB_SYSCGROUP_TTY
|
|
||||||
modify_ldt QSSB_SYSCGROUP_PROCESS
|
|
||||||
pivot_root QSSB_SYSCGROUP_CHROOT
|
|
||||||
_sysctl QSSB_SYSCGROUP_SYS
|
|
||||||
prctl QSSB_SYSCGROUP_PROCESS
|
|
||||||
arch_prctl QSSB_SYSCGROUP_PROCESS
|
|
||||||
adjtimex QSSB_SYSCGROUP_CLOCK
|
|
||||||
setrlimit QSSB_SYSCGROUP_RES
|
|
||||||
chroot QSSB_SYSCGROUP_CHROOT,QSSB_SYSCGROUP_FS
|
|
||||||
sync QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
acct QSSB_SYSCGROUP_PROCESS
|
|
||||||
settimeofday QSSB_SYSCGROUP_TIME
|
|
||||||
mount QSSB_SYSCGROUP_MOUNT,QSSB_SYSCGROUP_FS
|
|
||||||
umount2 QSSB_SYSCGROUP_UMOUNT,QSSB_SYSCGROUP_FS
|
|
||||||
swapon QSSB_SYSCGROUP_SWAP
|
|
||||||
swapoff QSSB_SYSCGROUP_SWAP
|
|
||||||
reboot QSSB_SYSCGROUP_POWER
|
|
||||||
sethostname QSSB_SYSCGROUP_HOST
|
|
||||||
setdomainname QSSB_SYSCGROUP_HOST
|
|
||||||
iopl QSSB_SYSCGROUP_IOPL
|
|
||||||
ioperm QSSB_SYSCGROUP_IOPL
|
|
||||||
create_module QSSB_SYSCGROUP_KMOD
|
|
||||||
init_module QSSB_SYSCGROUP_KMOD
|
|
||||||
delete_module QSSB_SYSCGROUP_KMOD
|
|
||||||
get_kernel_syms QSSB_SYSCGROUP_KMOD
|
|
||||||
query_module QSSB_SYSCGROUP_KMOD
|
|
||||||
quotactl QSSB_SYSCGROUP_QUOTA
|
|
||||||
nfsservctl QSSB_SYSCGROUP_NONE
|
|
||||||
getpmsg QSSB_SYSCGROUP_UNIMPLEMENTED
|
|
||||||
putpmsg QSSB_SYSCGROUP_UNIMPLEMENTED
|
|
||||||
afs_syscall QSSB_SYSCGROUP_UNIMPLEMENTED
|
|
||||||
tuxcall QSSB_SYSCGROUP_UNIMPLEMENTED
|
|
||||||
security QSSB_SYSCGROUP_UNIMPLEMENTED
|
|
||||||
gettid QSSB_SYSCGROUP_ID,QSSB_SYSCGROUP_THREAD
|
|
||||||
readahead QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_FS
|
|
||||||
setxattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_FS
|
|
||||||
lsetxattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_FS
|
|
||||||
fsetxattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_FS
|
|
||||||
getxattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
lgetxattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
fgetxattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
listxattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_FS
|
|
||||||
llistxattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_FS
|
|
||||||
flistxattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_FS
|
|
||||||
removexattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_FS
|
|
||||||
lremovexattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_FS
|
|
||||||
fremovexattr QSSB_SYSCGROUP_XATTR,QSSB_SYSCGROUP_FS
|
|
||||||
tkill QSSB_SYSCGROUP_THREAD,QSSB_SYSCGROUP_SIGNAL
|
|
||||||
time QSSB_SYSCGROUP_TIME
|
|
||||||
futex QSSB_SYSCGROUP_THREAD,QSSB_SYSCGROUP_FUTEX
|
|
||||||
sched_setaffinity QSSB_SYSCGROUP_SCHED
|
|
||||||
sched_getaffinity QSSB_SYSCGROUP_SCHED
|
|
||||||
set_thread_area QSSB_SYSCGROUP_THREAD
|
|
||||||
io_setup QSSB_SYSCGROUP_IO
|
|
||||||
io_destroy QSSB_SYSCGROUP_IO
|
|
||||||
io_getevents QSSB_SYSCGROUP_IO
|
|
||||||
io_submit QSSB_SYSCGROUP_IO
|
|
||||||
io_cancel QSSB_SYSCGROUP_IO
|
|
||||||
get_thread_area QSSB_SYSCGROUP_THREAD
|
|
||||||
lookup_dcookie QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_FS
|
|
||||||
epoll_create QSSB_SYSCGROUP_STDIO
|
|
||||||
epoll_ctl_old QSSB_SYSCGROUP_STDIO
|
|
||||||
epoll_wait_old QSSB_SYSCGROUP_STDIO
|
|
||||||
remap_file_pages QSSB_SYSCGROUP_NONE
|
|
||||||
getdents64 QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_FS
|
|
||||||
set_tid_address QSSB_SYSCGROUP_THREAD
|
|
||||||
restart_syscall QSSB_SYSCGROUP_SYSCALL
|
|
||||||
semtimedop QSSB_SYSCGROUP_SEM
|
|
||||||
fadvise64 QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_FD
|
|
||||||
timer_create QSSB_SYSCGROUP_TIMER
|
|
||||||
timer_settime QSSB_SYSCGROUP_TIMER
|
|
||||||
timer_gettime QSSB_SYSCGROUP_TIMER
|
|
||||||
timer_getoverrun QSSB_SYSCGROUP_TIMER
|
|
||||||
timer_delete QSSB_SYSCGROUP_TIMER
|
|
||||||
clock_settime QSSB_SYSCGROUP_TIME
|
|
||||||
clock_gettime QSSB_SYSCGROUP_TIME
|
|
||||||
clock_getres QSSB_SYSCGROUP_TIME
|
|
||||||
clock_nanosleep QSSB_SYSCGROUP_TIME
|
|
||||||
exit_group QSSB_SYSCGROUP_EXIT,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
epoll_wait QSSB_SYSCGROUP_FD
|
|
||||||
epoll_ctl QSSB_SYSCGROUP_FD
|
|
||||||
tgkill QSSB_SYSCGROUP_SIGNAL,QSSB_SYSCGROUP_THREAD
|
|
||||||
utimes QSSB_SYSCGROUP_PATH
|
|
||||||
vserver QSSB_SYSCGROUP_UNIMPLEMENTED
|
|
||||||
mbind QSSB_SYSCGROUP_MEMORY
|
|
||||||
set_mempolicy QSSB_SYSCGROUP_MEMORY
|
|
||||||
get_mempolicy QSSB_SYSCGROUP_MEMORY
|
|
||||||
mq_open QSSB_SYSCGROUP_MQ,QSSB_SYSCGROUP_IPC
|
|
||||||
mq_unlink QSSB_SYSCGROUP_MQ,QSSB_SYSCGROUP_IPC
|
|
||||||
mq_timedsend QSSB_SYSCGROUP_MQ,QSSB_SYSCGROUP_IPC
|
|
||||||
mq_timedreceive QSSB_SYSCGROUP_MQ,QSSB_SYSCGROUP_IPC
|
|
||||||
mq_notify QSSB_SYSCGROUP_MQ,QSSB_SYSCGROUP_IPC
|
|
||||||
mq_getsetattr QSSB_SYSCGROUP_MQ,QSSB_SYSCGROUP_IPC
|
|
||||||
kexec_load QSSB_SYSCGROUP_KEXEC
|
|
||||||
waitid QSSB_SYSCGROUP_SIGNAL
|
|
||||||
add_key QSSB_SYSCGROUP_KEYS
|
|
||||||
request_key QSSB_SYSCGROUP_KEYS
|
|
||||||
keyctl QSSB_SYSCGROUP_KEYS
|
|
||||||
ioprio_set QSSB_SYSCGROUP_PRIO
|
|
||||||
ioprio_get QSSB_SYSCGROUP_PRIO
|
|
||||||
inotify_init QSSB_SYSCGROUP_INOTIFY
|
|
||||||
inotify_add_watch QSSB_SYSCGROUP_INOTIFY
|
|
||||||
inotify_rm_watch QSSB_SYSCGROUP_INOTIFY
|
|
||||||
migrate_pages QSSB_SYSCGROUP_PROCESS
|
|
||||||
openat QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
mkdirat QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
mknodat QSSB_SYSCGROUP_DEV,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
fchownat QSSB_SYSCGROUP_PERMS,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
futimesat QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
newfstatat QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
unlinkat QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
renameat QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
linkat QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
symlinkat QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
readlinkat QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
fchmodat QSSB_SYSCGROUP_PERMS,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
faccessat QSSB_SYSCGROUP_PERMS,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
pselect6 QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
ppoll QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW,QSSB_SYSCGROUP_FS
|
|
||||||
unshare QSSB_SYSCGROUP_NS,QSSB_SYSCGROUP_FS
|
|
||||||
set_robust_list QSSB_SYSCGROUP_FUTEX
|
|
||||||
get_robust_list QSSB_SYSCGROUP_FUTEX
|
|
||||||
splice QSSB_SYSCGROUP_FD
|
|
||||||
tee QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
sync_file_range QSSB_SYSCGROUP_FD
|
|
||||||
vmsplice QSSB_SYSCGROUP_FD
|
|
||||||
move_pages QSSB_SYSCGROUP_PROCESS
|
|
||||||
utimensat QSSB_SYSCGROUP_PATH
|
|
||||||
epoll_pwait QSSB_SYSCGROUP_STDIO
|
|
||||||
signalfd QSSB_SYSCGROUP_SIGNAL
|
|
||||||
timerfd_create QSSB_SYSCGROUP_TIMER
|
|
||||||
eventfd QSSB_SYSCGROUP_FD
|
|
||||||
fallocate QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_FD
|
|
||||||
timerfd_settime QSSB_SYSCGROUP_TIMER
|
|
||||||
timerfd_gettime QSSB_SYSCGROUP_TIMER
|
|
||||||
accept4 QSSB_SYSCGROUP_SOCKET
|
|
||||||
signalfd4 QSSB_SYSCGROUP_FD
|
|
||||||
eventfd2 QSSB_SYSCGROUP_FD
|
|
||||||
epoll_create1 QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
dup3 QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
pipe2 QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
inotify_init1 QSSB_SYSCGROUP_INOTIFY
|
|
||||||
preadv QSSB_SYSCGROUP_STDIO
|
|
||||||
pwritev QSSB_SYSCGROUP_STDIO
|
|
||||||
rt_tgsigqueueinfo QSSB_SYSCGROUP_RT
|
|
||||||
perf_event_open QSSB_SYSCGROUP_PERF
|
|
||||||
recvmmsg QSSB_SYSCGROUP_SOCKET
|
|
||||||
fanotify_init QSSB_SYSCGROUP_FANOTIFY
|
|
||||||
fanotify_mark QSSB_SYSCGROUP_FANOTIFY
|
|
||||||
prlimit64 QSSB_SYSCGROUP_RES
|
|
||||||
name_to_handle_at QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_FS
|
|
||||||
open_by_handle_at QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_FS
|
|
||||||
clock_adjtime QSSB_SYSCGROUP_CLOCK
|
|
||||||
syncfs QSSB_SYSCGROUP_FD
|
|
||||||
sendmmsg QSSB_SYSCGROUP_SOCKET
|
|
||||||
setns QSSB_SYSCGROUP_NS
|
|
||||||
getcpu QSSB_SYSCGROUP_SCHED
|
|
||||||
#maybe IPC, but feels wrong
|
|
||||||
process_vm_readv QSSB_SYSCGROUP_NONE
|
|
||||||
process_vm_writev QSSB_SYSCGROUP_NONE
|
|
||||||
kcmp QSSB_SYSCGROUP_NONE
|
|
||||||
finit_module QSSB_SYSCGROUP_KMOD
|
|
||||||
sched_setattr QSSB_SYSCGROUP_SCHED
|
|
||||||
sched_getattr QSSB_SYSCGROUP_SCHED,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
renameat2 QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
seccomp QSSB_SYSCGROUP_NONE
|
|
||||||
getrandom QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
memfd_create QSSB_SYSCGROUP_MEMORY,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
kexec_file_load QSSB_SYSCGROUP_KEXEC
|
|
||||||
bpf QSSB_SYSCGROUP_NONE
|
|
||||||
execveat QSSB_SYSCGROUP_EXEC
|
|
||||||
userfaultfd QSSB_SYSCGROUP_NONE
|
|
||||||
membarrier QSSB_SYSCGROUP_NONE
|
|
||||||
mlock2 QSSB_SYSCGROUP_MEMORY
|
|
||||||
copy_file_range QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_DEFAULT_ALLOW
|
|
||||||
preadv2 QSSB_SYSCGROUP_STDIO
|
|
||||||
pwritev2 QSSB_SYSCGROUP_STDIO
|
|
||||||
#Those are newer than 5.10, wrap them in ifndef so we can compile on old systems
|
|
||||||
pkey_mprotect QSSB_SYSCGROUP_PKEY genifndef(329)
|
|
||||||
pkey_alloc QSSB_SYSCGROUP_PKEY genifndef(330)
|
|
||||||
pkey_free QSSB_SYSCGROUP_PKEY genifndef(331)
|
|
||||||
statx QSSB_SYSCGROUP_STAT,QSSB_SYSCGROUP_DEFAULT_ALLOW genifndef(332)
|
|
||||||
io_pgetevents QSSB_SYSCGROUP_NONE genifndef(333)
|
|
||||||
rseq QSSB_SYSCGROUP_THREAD genifndef(334)
|
|
||||||
pidfd_send_signal QSSB_SYSCGROUP_PIDFD genifndef(424)
|
|
||||||
io_uring_setup QSSB_SYSCGROUP_IOURING genifndef(425)
|
|
||||||
io_uring_enter QSSB_SYSCGROUP_IOURING genifndef(426)
|
|
||||||
io_uring_register QSSB_SYSCGROUP_IOURING genifndef(427)
|
|
||||||
open_tree QSSB_SYSCGROUP_NEWMOUNT genifndef(428)
|
|
||||||
move_mount QSSB_SYSCGROUP_NEWMOUNT genifndef(429)
|
|
||||||
fsopen QSSB_SYSCGROUP_NEWMOUNT genifndef(430)
|
|
||||||
fsconfig QSSB_SYSCGROUP_NEWMOUNT genifndef(431)
|
|
||||||
fsmount QSSB_SYSCGROUP_NEWMOUNT genifndef(432)
|
|
||||||
fspick QSSB_SYSCGROUP_NEWMOUNT genifndef(433)
|
|
||||||
pidfd_open QSSB_SYSCGROUP_PIDFD genifndef(434)
|
|
||||||
clone3 QSSB_SYSCGROUP_CLONE,QSSB_SYSCGROUP_DEFAULT_ALLOW genifndef(435)
|
|
||||||
close_range QSSB_SYSCGROUP_STDIO,QSSB_SYSCGROUP_DEFAULT_ALLOW genifndef(436)
|
|
||||||
openat2 QSSB_SYSCGROUP_FD,QSSB_SYSCGROUP_PATH,QSSB_SYSCGROUP_DEFAULT_ALLOW genifndef(437)
|
|
||||||
pidfd_getfd QSSB_SYSCGROUP_PIDFD genifndef(438)
|
|
||||||
faccessat2 QSSB_SYSCGROUP_PERMS,QSSB_SYSCGROUP_DEFAULT_ALLOW genifndef(439)
|
|
||||||
process_madvise QSSB_SYSCGROUP_MEMORY genifndef(440)
|
|
||||||
epoll_pwait2 QSSB_SYSCGROUP_STDIO genifndef(441)
|
|
||||||
mount_setattr QSSB_SYSCGROUP_NONE genifndef(442)
|
|
||||||
quotactl_fd QSSB_SYSCGROUP_QUOTA genifndef(443)
|
|
||||||
landlock_create_ruleset QSSB_SYSCGROUP_LANDLOCK genifndef(444)
|
|
||||||
landlock_add_rule QSSB_SYSCGROUP_LANDLOCK genifndef(445)
|
|
||||||
landlock_restrict_self QSSB_SYSCGROUP_LANDLOCK genifndef(446)
|
|
||||||
memfd_secret QSSB_SYSCGROUP_NONE genifndef(447)
|
|
||||||
process_mrelease QSSB_SYSCGROUP_NONE genifndef(448)
|
|
542
test.c
542
test.c
@ -1,4 +1,4 @@
|
|||||||
#include "qssb.h"
|
#include "exile.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
@ -6,12 +6,14 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
int xqssb_enable_policy(struct qssb_policy *policy)
|
#define LOG(...) do { fprintf(stdout, "%s(): ", __func__); fprintf(stdout, __VA_ARGS__); } while(0)
|
||||||
|
|
||||||
|
int xexile_enable_policy(struct exile_policy *policy)
|
||||||
{
|
{
|
||||||
int ret = qssb_enable_policy(policy);
|
int ret = exile_enable_policy(policy);
|
||||||
if(ret != 0)
|
if(ret != 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "qssb_enable_policy() failed: %i\n", ret);
|
LOG("failed: %i\n", ret);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -19,9 +21,8 @@ int xqssb_enable_policy(struct qssb_policy *policy)
|
|||||||
|
|
||||||
int test_default_main()
|
int test_default_main()
|
||||||
{
|
{
|
||||||
struct qssb_policy *policy = qssb_init_policy();
|
struct exile_policy *policy = exile_init_policy();
|
||||||
int ret = qssb_enable_policy(policy);
|
return xexile_enable_policy(policy);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int test_expected_kill(int (*f)())
|
static int test_expected_kill(int (*f)())
|
||||||
@ -39,16 +40,16 @@ static int test_expected_kill(int (*f)())
|
|||||||
int c = WTERMSIG(status);
|
int c = WTERMSIG(status);
|
||||||
if(c == SIGSYS)
|
if(c == SIGSYS)
|
||||||
{
|
{
|
||||||
printf("Got expected signal\n");
|
LOG("Got expected signal\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
printf("Unexpected status code: %i\n", c);
|
LOG("Unexpected status code: %i\n", c);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int c = WEXITSTATUS(status);
|
int c = WEXITSTATUS(status);
|
||||||
printf("Process was not killed, test fails. Status code of exit: %i\n", c);
|
LOG("Process was not killed, test fails. Status code of exit: %i\n", c);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -68,7 +69,7 @@ static int test_successful_exit(int (*f)())
|
|||||||
if(WIFSIGNALED(status))
|
if(WIFSIGNALED(status))
|
||||||
{
|
{
|
||||||
int c = WTERMSIG(status);
|
int c = WTERMSIG(status);
|
||||||
printf("Received signal, which was not expected. Signal was: %i\n", c);
|
LOG("Received signal, which was not expected. Signal was: %i\n", c);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -76,25 +77,25 @@ static int test_successful_exit(int (*f)())
|
|||||||
int c = WEXITSTATUS(status);
|
int c = WEXITSTATUS(status);
|
||||||
if(c != 0)
|
if(c != 0)
|
||||||
{
|
{
|
||||||
printf("Process failed to exit properly. Status code is: %i\n", c);
|
LOG("Process failed to exit properly. Status code is: %i\n", c);
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
printf("Process exited sucessfully as expected");
|
LOG("Process exited sucessfully as expected");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int do_test_seccomp_blacklisted()
|
static int do_test_seccomp_blacklisted()
|
||||||
{
|
{
|
||||||
struct qssb_policy *policy = qssb_init_policy();
|
struct exile_policy *policy = exile_init_policy();
|
||||||
qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(getuid));
|
exile_append_syscall_policy(policy,EXILE_SYS(getuid), EXILE_SYSCALL_DENY_KILL_PROCESS, NULL, 0);
|
||||||
qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW);
|
exile_append_syscall_default_policy(policy, EXILE_SYSCALL_ALLOW);
|
||||||
|
|
||||||
xqssb_enable_policy(policy);
|
xexile_enable_policy(policy);
|
||||||
|
|
||||||
uid_t pid = geteuid();
|
uid_t pid = syscall(EXILE_SYS(geteuid));
|
||||||
pid = getuid();
|
pid = syscall(EXILE_SYS(getuid));
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
||||||
@ -107,14 +108,14 @@ int test_seccomp_blacklisted()
|
|||||||
|
|
||||||
static int do_test_seccomp_blacklisted_call_permitted()
|
static int do_test_seccomp_blacklisted_call_permitted()
|
||||||
{
|
{
|
||||||
struct qssb_policy *policy = qssb_init_policy();
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
|
||||||
qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(getuid));
|
exile_append_syscall_policy(policy, EXILE_SYS(getuid), EXILE_SYSCALL_DENY_KILL_PROCESS, NULL, 0);
|
||||||
qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW);
|
exile_append_syscall_default_policy(policy, EXILE_SYSCALL_ALLOW);
|
||||||
|
|
||||||
int ret = qssb_enable_policy(policy);
|
xexile_enable_policy(policy);
|
||||||
//geteuid is not blacklisted, so must succeed
|
//geteuid is not blacklisted, so must succeed
|
||||||
uid_t pid = geteuid();
|
uid_t pid = syscall(EXILE_SYS(geteuid));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,15 +127,15 @@ int test_seccomp_blacklisted_call_permitted()
|
|||||||
|
|
||||||
static int do_test_seccomp_x32_kill()
|
static int do_test_seccomp_x32_kill()
|
||||||
{
|
{
|
||||||
struct qssb_policy *policy = qssb_init_policy();
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
|
||||||
qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(getuid));
|
exile_append_syscall_policy(policy, EXILE_SYS(getuid), EXILE_SYSCALL_DENY_KILL_PROCESS, NULL, 0);
|
||||||
qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW);
|
exile_append_syscall_default_policy(policy, EXILE_SYSCALL_ALLOW);
|
||||||
|
|
||||||
xqssb_enable_policy(policy);
|
xexile_enable_policy(policy);
|
||||||
|
|
||||||
/* Attempt to bypass by falling back to x32 should be blocked */
|
/* Attempt to bypass by falling back to x32 should be blocked */
|
||||||
syscall(QSSB_SYS(getuid)+__X32_SYSCALL_BIT);
|
syscall(EXILE_SYS(getuid)+__X32_SYSCALL_BIT);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -147,14 +148,14 @@ int test_seccomp_x32_kill()
|
|||||||
/* Tests whether seccomp rules end with a policy matching all syscalls */
|
/* Tests whether seccomp rules end with a policy matching all syscalls */
|
||||||
int test_seccomp_require_last_matchall()
|
int test_seccomp_require_last_matchall()
|
||||||
{
|
{
|
||||||
struct qssb_policy *policy = qssb_init_policy();
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
|
||||||
qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(getuid));
|
exile_append_syscall_policy(policy, EXILE_SYS(getuid), EXILE_SYSCALL_DENY_KILL_PROCESS, NULL, 0);
|
||||||
|
|
||||||
int status = qssb_enable_policy(policy);
|
int status = exile_enable_policy(policy);
|
||||||
if(status == 0)
|
if(status == 0)
|
||||||
{
|
{
|
||||||
printf("Failed. Should not have been enabled!");
|
LOG("Failed. Should not have been enabled!");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -162,16 +163,16 @@ int test_seccomp_require_last_matchall()
|
|||||||
|
|
||||||
static int do_test_seccomp_errno()
|
static int do_test_seccomp_errno()
|
||||||
{
|
{
|
||||||
struct qssb_policy *policy = qssb_init_policy();
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
|
||||||
qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_RET_ERROR, QSSB_SYS(close));
|
exile_append_syscall_policy(policy, EXILE_SYS(close),EXILE_SYSCALL_DENY_RET_ERROR, NULL, 0);
|
||||||
qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW);
|
exile_append_syscall_default_policy(policy, EXILE_SYSCALL_ALLOW);
|
||||||
|
|
||||||
xqssb_enable_policy(policy);
|
xexile_enable_policy(policy);
|
||||||
uid_t id = getuid();
|
uid_t id = syscall(EXILE_SYS(getuid));
|
||||||
|
|
||||||
int fd = close(0);
|
int fd = syscall(EXILE_SYS(close), 0);
|
||||||
printf("close() return code: %i, errno: %s\n", fd, strerror(errno));
|
LOG("close() return code: %i, errno: %s\n", fd, strerror(errno));
|
||||||
return fd == -1 ? 0 : 1;
|
return fd == -1 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,29 +183,232 @@ int test_seccomp_errno()
|
|||||||
return test_successful_exit(&do_test_seccomp_errno);
|
return test_successful_exit(&do_test_seccomp_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int test_seccomp_group()
|
int test_seccomp_argfilter_allowed()
|
||||||
{
|
{
|
||||||
struct qssb_policy *policy = qssb_init_policy();
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
|
||||||
qssb_append_group_syscall_policy(policy, QSSB_SYSCALL_DENY_RET_ERROR, QSSB_SYSCGROUP_SOCKET);
|
struct sock_filter argfilter[2] =
|
||||||
qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW);
|
|
||||||
|
|
||||||
xqssb_enable_policy(policy);
|
|
||||||
|
|
||||||
int s = socket(AF_INET,SOCK_STREAM,0);
|
|
||||||
if(s != -1)
|
|
||||||
{
|
{
|
||||||
printf("Failed: socket was expected to return error\n");
|
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, args[1]))),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, O_WRONLY, 0, EXILE_SYSCALL_EXIT_BPF_NO_MATCH)
|
||||||
|
};
|
||||||
|
|
||||||
|
exile_append_syscall_policy(policy, EXILE_SYS(open),EXILE_SYSCALL_DENY_RET_ERROR, argfilter, 2);
|
||||||
|
exile_append_syscall_default_policy(policy, EXILE_SYSCALL_ALLOW);
|
||||||
|
xexile_enable_policy(policy);
|
||||||
|
|
||||||
|
|
||||||
|
char *t = "/dev/random";
|
||||||
|
int ret = (int) syscall(EXILE_SYS(open),t, O_RDONLY);
|
||||||
|
|
||||||
|
if(ret == -1)
|
||||||
|
{
|
||||||
|
printf("Failed: open was expected to succeed, but returned %i\n", ret);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int test_seccomp_argfilter_filtered()
|
||||||
|
{
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
|
||||||
|
struct sock_filter argfilter[2] =
|
||||||
|
{
|
||||||
|
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, args[1]))),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, O_WRONLY, 0, EXILE_SYSCALL_EXIT_BPF_NO_MATCH)
|
||||||
|
};
|
||||||
|
|
||||||
|
exile_append_syscall_policy(policy, EXILE_SYS(open),EXILE_SYSCALL_DENY_RET_ERROR, argfilter, 2);
|
||||||
|
exile_append_syscall_default_policy(policy, EXILE_SYSCALL_ALLOW);
|
||||||
|
xexile_enable_policy(policy);
|
||||||
|
|
||||||
|
char *t = "/dev/random";
|
||||||
|
int ret = (int) syscall(EXILE_SYS(open),t, O_WRONLY);
|
||||||
|
|
||||||
|
if(ret != -1)
|
||||||
|
{
|
||||||
|
printf("Failed: open was expected to fail, but returned %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int test_seccomp_argfilter_mixed()
|
||||||
|
{
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
|
||||||
|
struct sock_filter argfilter[2] =
|
||||||
|
{
|
||||||
|
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, args[1]))),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, O_WRONLY, 0, EXILE_SYSCALL_EXIT_BPF_NO_MATCH)
|
||||||
|
};
|
||||||
|
|
||||||
|
exile_append_syscall_policy(policy, EXILE_SYS(stat),EXILE_SYSCALL_DENY_RET_ERROR, NULL,0);
|
||||||
|
exile_append_syscall_policy(policy, EXILE_SYS(open),EXILE_SYSCALL_DENY_RET_ERROR, argfilter, 2);
|
||||||
|
exile_append_syscall_policy(policy, EXILE_SYS(getpid),EXILE_SYSCALL_DENY_RET_ERROR, NULL, 0);
|
||||||
|
|
||||||
|
exile_append_syscall_default_policy(policy, EXILE_SYSCALL_ALLOW);
|
||||||
|
xexile_enable_policy(policy);
|
||||||
|
|
||||||
|
struct stat statbuf;
|
||||||
|
int s = (int) syscall(EXILE_SYS(stat), "/dev/urandom", &statbuf);
|
||||||
|
if(s != -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: stat was expected to fail, but returned %i\n", s);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t p = (pid_t) syscall(EXILE_SYS(getpid));
|
||||||
|
if(p != -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: getpid was expected to fail, but returned %i\n", p);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *t = "/dev/random";
|
||||||
|
int ret = (int) syscall(EXILE_SYS(open),t, O_WRONLY);
|
||||||
|
if(ret != -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: open was expected to fail, but returned %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ret = (int) syscall(EXILE_SYS(open), t, O_RDONLY);
|
||||||
|
if(ret == -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: open with O_RDONLY was expected to succeed, but returned %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int do_test_seccomp_vow_socket()
|
||||||
|
{
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
policy->vow_promises = EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_INET | EXILE_SYSCALL_VOW_DENY_ERROR;
|
||||||
|
xexile_enable_policy(policy);
|
||||||
|
|
||||||
|
int s = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if(s == -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: socket was expected to succeed, but returned %i\n", s);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
s = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||||
|
if(s != -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: socket was expected to fail, but returned %i\n", s);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_test_seccomp_vow_open()
|
||||||
|
{
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
policy->vow_promises = EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_RPATH | EXILE_SYSCALL_VOW_DENY_ERROR;
|
||||||
|
xexile_enable_policy(policy);
|
||||||
|
|
||||||
|
int ret = open("/dev/urandom", O_WRONLY | O_APPEND);
|
||||||
|
if(ret != -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: open was expected to fail, but returned %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ret = open("/dev/urandom", O_RDWR);
|
||||||
|
if(ret != -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: open O_RDWR was expected to fail, but returned %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ret = open("/dev/urandom", O_RDONLY);
|
||||||
|
if(ret == -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: open was expected to succceed, but returned %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_seccomp_vow()
|
||||||
|
{
|
||||||
|
int ret = test_successful_exit(&do_test_seccomp_vow_open);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
LOG("Failed: do_test_seccomp_vow_open()\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ret = test_successful_exit(&do_test_seccomp_vow_socket);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
LOG("Failed: do_test_seccomp_vow_socket()\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_seccomp_exile_vow_multiple()
|
||||||
|
{
|
||||||
|
|
||||||
|
int ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_UNIX | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
LOG("Failed: exile_vow() call 1 failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if(s == -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: socket was expected to succeed, but returned %i\n", s);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Let's take away unix sockets, so it should not be possible anymore */
|
||||||
|
ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
LOG("Failed: exile_vow() call 2 failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if(s != -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: socket was expected to fail, but returned %i\n", s);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Let's try to regain unix sockets again */
|
||||||
|
ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_UNIX | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
LOG("Failed: exile_vow() call 3 failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if(s != -1)
|
||||||
|
{
|
||||||
|
LOG("Failed: socket was still expected to fail, but returned %i\n", s);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if HAVE_LANDLOCK == 1
|
||||||
int test_landlock()
|
int test_landlock()
|
||||||
{
|
{
|
||||||
struct qssb_policy *policy = qssb_init_policy();
|
if(!exile_landlock_is_available())
|
||||||
qssb_append_path_policy(policy, QSSB_FS_ALLOW_READ, "/proc/self/fd");
|
{
|
||||||
int ret = qssb_enable_policy(policy);
|
LOG("landlock not available, so cannot test\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ, "/proc/self/fd");
|
||||||
|
xexile_enable_policy(policy);
|
||||||
|
|
||||||
int fd = open("/", O_RDONLY | O_CLOEXEC);
|
int fd = open("/", O_RDONLY | O_CLOEXEC);
|
||||||
if(fd < 0)
|
if(fd < 0)
|
||||||
{
|
{
|
||||||
@ -215,9 +419,10 @@ int test_landlock()
|
|||||||
|
|
||||||
int test_landlock_deny_write()
|
int test_landlock_deny_write()
|
||||||
{
|
{
|
||||||
struct qssb_policy *policy = qssb_init_policy();
|
struct exile_policy *policy = exile_init_policy();
|
||||||
qssb_append_path_policy(policy, QSSB_FS_ALLOW_READ, "/tmp/");
|
exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ, "/tmp/");
|
||||||
int ret = qssb_enable_policy(policy);
|
xexile_enable_policy(policy);
|
||||||
|
|
||||||
int fd = open("/tmp/a", O_WRONLY | O_CLOEXEC);
|
int fd = open("/tmp/a", O_WRONLY | O_CLOEXEC);
|
||||||
if(fd < 0)
|
if(fd < 0)
|
||||||
{
|
{
|
||||||
@ -225,30 +430,35 @@ int test_landlock_deny_write()
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
int test_landlock()
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_landlock_deny_write()
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int test_nofs()
|
int test_nofs()
|
||||||
{
|
{
|
||||||
struct qssb_policy *policy = qssb_init_policy();
|
struct exile_policy *policy = exile_init_policy();
|
||||||
policy->no_fs = 1;
|
policy->no_fs = 1;
|
||||||
|
xexile_enable_policy(policy);
|
||||||
int ret = qssb_enable_policy(policy);
|
|
||||||
if(ret != 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to activate nofs sandbox\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int s = socket(AF_INET,SOCK_STREAM,0);
|
int s = socket(AF_INET,SOCK_STREAM,0);
|
||||||
if(s == -1)
|
if(s == -1)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed to open socket but this was not requested by policy\n");
|
LOG("Failed to open socket but this was not requested by policy\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Expect seccomp to take care of this */
|
/* Expect seccomp to take care of this */
|
||||||
if(open("/test", O_CREAT | O_WRONLY) >= 0)
|
if(open("/test", O_CREAT | O_WRONLY) >= 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed: We do not expect write access\n");
|
LOG("Failed: We do not expect write access\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,26 +468,20 @@ int test_nofs()
|
|||||||
|
|
||||||
int test_no_new_fds()
|
int test_no_new_fds()
|
||||||
{
|
{
|
||||||
struct qssb_policy *policy = qssb_init_policy();
|
struct exile_policy *policy = exile_init_policy();
|
||||||
policy->no_new_fds = 1;
|
policy->no_new_fds = 1;
|
||||||
|
xexile_enable_policy(policy);
|
||||||
int ret = qssb_enable_policy(policy);
|
|
||||||
if(ret != 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Failed to activate no_new_fd sandbox\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(open("/tmp/test", O_CREAT | O_WRONLY) >= 0)
|
if(open("/tmp/test", O_CREAT | O_WRONLY) >= 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed: Could open new file descriptor\n");
|
LOG("Failed: Could open new file descriptor\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int s = socket(AF_INET,SOCK_STREAM,0);
|
int s = socket(AF_INET,SOCK_STREAM,0);
|
||||||
if(s >= 0)
|
if(s >= 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed: socket got opened but policy denied\n");
|
LOG("Failed: socket got opened but policy denied\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,6 +489,178 @@ int test_no_new_fds()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern int mkpath(const char *p, mode_t mode, int baseisfile);
|
||||||
|
int test_mkpath()
|
||||||
|
{
|
||||||
|
system("rm -rf /tmp/.exile.h/");
|
||||||
|
const char *filepath = "/tmp/.exile.h/test_mkpath/some/sub/dir/file";
|
||||||
|
const char *dirpath = "/tmp/.exile.h/test_mkpath/some/other/sub/dir";
|
||||||
|
int ret = mkpath(filepath, 0700, 1);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
LOG("Failed: mkpath(file) returned: %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ret = mkpath(dirpath, 0700, 0);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
LOG("Failed: mkpath(dirpath) returned: %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat statbuf;
|
||||||
|
ret = stat(filepath, &statbuf);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
LOG("Failed: stat on filepath returned: %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(!S_ISREG(statbuf.st_mode))
|
||||||
|
{
|
||||||
|
LOG("Failed: mkpath did not create a file: %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ret = stat(dirpath, &statbuf);
|
||||||
|
if(ret != 0)
|
||||||
|
{
|
||||||
|
LOG("Failed: stat on dirpath returned: %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(!S_ISDIR(statbuf.st_mode))
|
||||||
|
{
|
||||||
|
LOG("Failed: mkpath did not create a directory: %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
system("rm -rf /tmp/.exile.h/");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_fail_flags()
|
||||||
|
{
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ, "/nosuchpathexists");
|
||||||
|
int ret = exile_enable_policy(policy);
|
||||||
|
if(ret == 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed: A path that does not exist should have set the error flag %i\n", ret);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int *read_pipe = NULL;
|
||||||
|
int do_launch_test(void *arg)
|
||||||
|
{
|
||||||
|
int num = *(int *)(arg);
|
||||||
|
num += 1;
|
||||||
|
char buffer[512] = { 0 };
|
||||||
|
read(*read_pipe, buffer, sizeof(buffer)-1);
|
||||||
|
printf("Sandboxed +1: %i\n", num);
|
||||||
|
printf("Echoing: %s\n", buffer);
|
||||||
|
fflush(stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_launch()
|
||||||
|
{
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
struct exile_launch_params params = { 0 };
|
||||||
|
struct exile_launch_result res = {0};
|
||||||
|
int num = 22;
|
||||||
|
params.func = &do_launch_test;
|
||||||
|
params.funcarg = #
|
||||||
|
params.policy = policy;
|
||||||
|
read_pipe = ¶ms.child_write_pipe[0];
|
||||||
|
int launchfd = exile_launch(¶ms, &res);
|
||||||
|
if(launchfd < 0)
|
||||||
|
{
|
||||||
|
LOG("Failed to launch\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buffer[4096] = { 0 };
|
||||||
|
write(res.write_fd, "1234", 4);
|
||||||
|
int s = read(res.read_fd, buffer, sizeof(buffer)-1);
|
||||||
|
write(1, buffer, s);
|
||||||
|
LOG("Before wait, got: %i\n", s);
|
||||||
|
fflush(stdout);
|
||||||
|
if(strstr(buffer, "Echoing: 1234") == NULL)
|
||||||
|
{
|
||||||
|
LOG("Failed: Did not get back what we wrote\n");
|
||||||
|
}
|
||||||
|
int status = 0;
|
||||||
|
waitpid(res.tid, &status, __WALL);
|
||||||
|
if(WIFEXITED(status))
|
||||||
|
{
|
||||||
|
status = WEXITSTATUS(status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LAUNCH_GET_TEST_STR "Control yourself. Take only what you need from it.\n"
|
||||||
|
int do_launch_get_test(void *a)
|
||||||
|
{
|
||||||
|
fprintf(stdout, LAUNCH_GET_TEST_STR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_launch_get()
|
||||||
|
{
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
struct exile_launch_params params = { 0 };
|
||||||
|
params.func = &do_launch_get_test;
|
||||||
|
params.funcarg = NULL;
|
||||||
|
params.policy = policy;
|
||||||
|
|
||||||
|
size_t n = 0;
|
||||||
|
char *content = exile_launch_get(¶ms, &n);
|
||||||
|
unsigned int len = strlen(LAUNCH_GET_TEST_STR);
|
||||||
|
if(n != strlen(LAUNCH_GET_TEST_STR))
|
||||||
|
{
|
||||||
|
LOG("Lenght does does not match: %lu vs %u\n", n, len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(strcmp(content, LAUNCH_GET_TEST_STR) != 0)
|
||||||
|
{
|
||||||
|
LOG("Received content differs\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_vows_from_str()
|
||||||
|
{
|
||||||
|
uint64_t expected = EXILE_SYSCALL_VOW_CHOWN | EXILE_SYSCALL_VOW_WPATH | EXILE_SYSCALL_VOW_INET | EXILE_SYSCALL_VOW_DENY_ERROR;
|
||||||
|
uint64_t actual = exile_vows_from_str("chown wpath inet error");
|
||||||
|
if(expected != actual)
|
||||||
|
{
|
||||||
|
LOG("Masks don't match: %lu vs %lu\n", expected, actual);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_clone3_nosys()
|
||||||
|
{
|
||||||
|
struct exile_policy *policy = exile_init_policy();
|
||||||
|
policy->vow_promises = exile_vows_from_str("stdio rpath wpath cpath thread error");
|
||||||
|
|
||||||
|
exile_enable_policy(policy);
|
||||||
|
/* While args are invalid, it should never reach clone3 syscall handler, so it's irrelevant for
|
||||||
|
our test*/
|
||||||
|
long ret = syscall(__NR_clone3, NULL, 0);
|
||||||
|
|
||||||
|
if(ret == -1 && errno != ENOSYS)
|
||||||
|
{
|
||||||
|
LOG("clone3() was not allowed but did not return ENOSYS. It returned: %li, errno: %i\n", ret, errno);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct dispatcher
|
struct dispatcher
|
||||||
{
|
{
|
||||||
char *name;
|
char *name;
|
||||||
@ -298,11 +674,21 @@ struct dispatcher dispatchers[] = {
|
|||||||
{ "seccomp-x32-kill", &test_seccomp_x32_kill},
|
{ "seccomp-x32-kill", &test_seccomp_x32_kill},
|
||||||
{ "seccomp-require-last-matchall", &test_seccomp_require_last_matchall},
|
{ "seccomp-require-last-matchall", &test_seccomp_require_last_matchall},
|
||||||
{ "seccomp-errno", &test_seccomp_errno},
|
{ "seccomp-errno", &test_seccomp_errno},
|
||||||
{ "seccomp-group", &test_seccomp_group},
|
{ "seccomp-argfilter-allowed", &test_seccomp_argfilter_allowed},
|
||||||
|
{ "seccomp-argfilter-filtered", &test_seccomp_argfilter_filtered},
|
||||||
|
{ "seccomp-argfilter-mixed", &test_seccomp_argfilter_mixed},
|
||||||
|
{ "seccomp-vow", &test_seccomp_vow},
|
||||||
|
{ "seccomp-vow-exile_vow-multi", &test_seccomp_exile_vow_multiple},
|
||||||
{ "landlock", &test_landlock},
|
{ "landlock", &test_landlock},
|
||||||
{ "landlock-deny-write", &test_landlock_deny_write },
|
{ "landlock-deny-write", &test_landlock_deny_write },
|
||||||
{ "no_fs", &test_nofs},
|
{ "no_fs", &test_nofs},
|
||||||
{ "no_new_fds", &test_no_new_fds}
|
{ "no_new_fds", &test_no_new_fds},
|
||||||
|
{ "mkpath", &test_mkpath},
|
||||||
|
{ "failflags", &test_fail_flags},
|
||||||
|
{ "launch", &test_launch},
|
||||||
|
{ "launch-get", &test_launch_get},
|
||||||
|
{ "vow_from_str", &test_vows_from_str},
|
||||||
|
{ "clone3_nosys", &test_clone3_nosys},
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
|
92
test.cpp
Normal file
92
test.cpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#include "exile.hpp"
|
||||||
|
#include "assert.h"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
std::string sandboxed_reverse(std::string str)
|
||||||
|
{
|
||||||
|
std::reverse(str.begin(), str.end());
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t stdstrlen(const std::string &str)
|
||||||
|
{
|
||||||
|
return str.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int incrementer(int arg)
|
||||||
|
{
|
||||||
|
return ++arg;
|
||||||
|
}
|
||||||
|
int test_exile_launch_trivial()
|
||||||
|
{
|
||||||
|
int u = 22;
|
||||||
|
int result = exile_launch<int>(exile_init_policy(), &incrementer, u);
|
||||||
|
assert(result == 23);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_exile_launch_stdstring()
|
||||||
|
{
|
||||||
|
std::string str = "abc123";
|
||||||
|
std::string reversed = exile_launch<std::string>(exile_init_policy(), &sandboxed_reverse, str);
|
||||||
|
assert(reversed == "321cba");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct not_trivially_copyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string somecontent;
|
||||||
|
};
|
||||||
|
|
||||||
|
int test_exile_launch_serializer()
|
||||||
|
{
|
||||||
|
static_assert(! std::is_trivially_copyable_v<not_trivially_copyable>);
|
||||||
|
|
||||||
|
auto serializer = [](const not_trivially_copyable &obj, char *buf, size_t n){
|
||||||
|
serialize_stdstring<std::string>(obj.somecontent, buf, n);
|
||||||
|
return obj.somecontent.size();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto deserializer = [](const char *buffer, size_t n) {
|
||||||
|
not_trivially_copyable obj;
|
||||||
|
obj.somecontent = deserialize_stdstring<std::string>(buffer, n);
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
not_trivially_copyable result = exile_launch<not_trivially_copyable>(exile_init_policy(), serializer, deserializer, []() {not_trivially_copyable obj; obj.somecontent = "Just something"; return obj;});
|
||||||
|
|
||||||
|
assert(result.somecontent == "Just something");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if(argc < 2)
|
||||||
|
{
|
||||||
|
std::cerr << "Missing test" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::map<std::string, int (*)()> map = {
|
||||||
|
{ "launch-trivial-cpp", &test_exile_launch_trivial} ,
|
||||||
|
{ "launch-stdstring-cpp", &test_exile_launch_stdstring },
|
||||||
|
{ "launch-serializer-cpp", &test_exile_launch_serializer },
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string test = argv[1];
|
||||||
|
if(test == "--dumptests")
|
||||||
|
{
|
||||||
|
for(auto &entry : map)
|
||||||
|
{
|
||||||
|
std::cout << entry.first << std::endl;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int (*fn)() = map[test];
|
||||||
|
if(fn != nullptr)
|
||||||
|
{
|
||||||
|
return fn();
|
||||||
|
}
|
||||||
|
std::cerr << "Unknown test" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
38
test.sh
38
test.sh
@ -1,10 +1,12 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
NC='\033[0m'
|
NC='\033[0m'
|
||||||
|
|
||||||
COUNT_SUCCEEDED=0
|
COUNT_SUCCEEDED=0
|
||||||
COUNT_FAILED=0
|
COUNT_FAILED=0
|
||||||
|
COUNT_SKIPPED=0
|
||||||
|
|
||||||
function print_fail()
|
function print_fail()
|
||||||
{
|
{
|
||||||
@ -16,6 +18,11 @@ function print_success()
|
|||||||
echo -e "${GREEN}$@${NC}"
|
echo -e "${GREEN}$@${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function print_skipped()
|
||||||
|
{
|
||||||
|
echo -e "${YELLOW}$@${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
function runtest_fail()
|
function runtest_fail()
|
||||||
{
|
{
|
||||||
print_fail "failed"
|
print_fail "failed"
|
||||||
@ -28,27 +35,37 @@ function runtest_success()
|
|||||||
COUNT_SUCCEEDED=$((COUNT_SUCCEEDED+1))
|
COUNT_SUCCEEDED=$((COUNT_SUCCEEDED+1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function runtest_skipped()
|
||||||
|
{
|
||||||
|
print_skipped "skipped"
|
||||||
|
COUNT_SKIPPED=$((COUNT_SKIPPED+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function runtest()
|
function runtest()
|
||||||
{
|
{
|
||||||
testname="$1"
|
testbin="$1"
|
||||||
test_log_file="$2"
|
testname="$2"
|
||||||
|
test_log_file="$3"
|
||||||
|
|
||||||
echo "Running: $testname. Date: $(date)" > "${test_log_file}"
|
echo "Running: $testname. Date: $(date)" > "${test_log_file}"
|
||||||
|
|
||||||
echo -n "Running $1... "
|
echo -n "Running $testname... "
|
||||||
#exit $? to suppress shell message like "./test.sh: line 18: pid Bad system call"
|
#exit $? to suppress shell message like "./test.sh: line 18: pid Bad system call"
|
||||||
(./test $1 || exit $?) &>> "${test_log_file}"
|
(./$testbin "$testname" || exit $?) &>> "${test_log_file}"
|
||||||
ret=$?
|
ret=$?
|
||||||
SUCCESS="no"
|
SUCCESS="no"
|
||||||
if [ $ret -eq 0 ] ; then
|
if [ $ret -eq 0 ] ; then
|
||||||
runtest_success
|
runtest_success
|
||||||
SUCCESS="yes"
|
SUCCESS="yes"
|
||||||
|
elif [ $ret -eq 2 ] ; then
|
||||||
|
runtest_skipped
|
||||||
|
SUCCESS="skipped"
|
||||||
else
|
else
|
||||||
runtest_fail
|
runtest_fail
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Finished: ${testname}. Date: $(date). Success: $SUCCESS" >> "${test_log_file}"
|
echo "Finished: ${testname} (${testbin}). Date: $(date). Success: $SUCCESS" >> "${test_log_file}"
|
||||||
}
|
}
|
||||||
|
|
||||||
GIT_ID=$( git log --pretty="format:%h" -n1 )
|
GIT_ID=$( git log --pretty="format:%h" -n1 )
|
||||||
@ -58,18 +75,23 @@ if [ -z "$LOG_OUTPUT_DIR" ] ; then
|
|||||||
LOG_OUTPUT_DIR="./logs/"
|
LOG_OUTPUT_DIR="./logs/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LOG_OUTPUT_DIR_PATH="${LOG_OUTPUT_DIR}/qssb_test_${GIT_ID}_${TIMESTAMP}"
|
LOG_OUTPUT_DIR_PATH="${LOG_OUTPUT_DIR}/exile_test_${GIT_ID}_${TIMESTAMP}"
|
||||||
[ -d "$LOG_OUTPUT_DIR_PATH" ] || mkdir -p "$LOG_OUTPUT_DIR_PATH"
|
[ -d "$LOG_OUTPUT_DIR_PATH" ] || mkdir -p "$LOG_OUTPUT_DIR_PATH"
|
||||||
|
|
||||||
for test in $( ./test --dumptests ) ; do
|
for test in $( ./test --dumptests ) ; do
|
||||||
testname=$( echo $test )
|
testname=$( echo $test )
|
||||||
runtest "$testname" "${LOG_OUTPUT_DIR_PATH}/log.${testname}"
|
runtest test "$testname" "${LOG_OUTPUT_DIR_PATH}/log.${testname}"
|
||||||
|
done
|
||||||
|
|
||||||
|
for test in $( ./testcpp --dumptests ) ; do
|
||||||
|
testname=$( echo $test )
|
||||||
|
runtest testcpp "$testname" "${LOG_OUTPUT_DIR_PATH}/log.${testname}"
|
||||||
done
|
done
|
||||||
echo
|
echo
|
||||||
echo "Tests finished. Logs in $(realpath ${LOG_OUTPUT_DIR_PATH})"
|
echo "Tests finished. Logs in $(realpath ${LOG_OUTPUT_DIR_PATH})"
|
||||||
echo "Succeeded: $COUNT_SUCCEEDED"
|
echo "Succeeded: $COUNT_SUCCEEDED"
|
||||||
echo "Failed: $COUNT_FAILED"
|
echo "Failed: $COUNT_FAILED"
|
||||||
|
echo "Skipped: $COUNT_SKIPPED"
|
||||||
|
|
||||||
if [ $COUNT_FAILED -gt 0 ] ; then
|
if [ $COUNT_FAILED -gt 0 ] ; then
|
||||||
exit 1
|
exit 1
|
||||||
|
Reference in New Issue
Block a user