16 次代码提交

作者 SHA1 备注 提交日期
73dae3a102 append_syscall_to_bpf(): Check for unlikely case of too many sock_filters 2022-03-17 15:17:28 +01:00
f2ca26010a exile.hpp: Mark do_clone inline, not static 2022-03-15 08:48:04 +01:00
0f39ee7061 Makefile: Build exile.o separately, link it in all tests 2022-03-15 08:48:04 +01:00
41bd6e8f10 exile.h: Retire static child_read/write_pipe vars 2022-03-15 08:48:04 +01:00
7f083909e6 exile.h: Move definitions to new file exile.c
Especially with exile_launch(), we will be included
from more than one translation unit. Thus, ODR becomes
a headache now.

So move definitions to exile.c.
2022-03-15 08:48:04 +01:00
732623fc6f exile.h: Add extern "C" guards 2022-03-15 08:48:04 +01:00
dcfbe641f9 c++: Add explicit exile_launch() std::basic_string variant 2022-03-15 08:48:04 +01:00
72a3b041d9 c++: Retire exile_launch_trivial(), use std::enable_if 2022-03-15 08:48:04 +01:00
c57ba807d7 Makefile: Add 'tests' target, depend on headers too to rebuild on changes of those 2022-03-15 08:48:04 +01:00
6f19c53acf test.sh: Also run C++ tests 2022-03-15 08:48:04 +01:00
99d26480d7 Add test.cpp to test C++ API 2022-03-15 08:48:04 +01:00
f13cff754c Begin C++ API: Add exile.hpp with exile_launch() wrappers 2022-03-15 08:48:04 +01:00
278ae31e2e fixup! Introduce exile_vows_from_str() 2022-01-30 10:45:05 +01:00
5ef54a08b4 struct syscall_vow_map: change 'str' to const char* 2022-01-30 10:42:46 +01:00
29b5864dd3 test: Introduce LOG(), avoid inconsistent printf/fprintf 2022-01-17 22:48:29 +01:00
0a4e4850f9 Introduce exile_vows_from_str() 2022-01-17 22:42:26 +01:00
共有 2 个文件被更改,包括 12 次插入123 次删除

123
README.md
查看文件

@ -6,124 +6,17 @@ The following section gives small quick examples. Then the motivation is explain
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.
TODO This section will demonstrate the simplicity of the API, but only serves as an overview.
### 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 folloing example,
ls is never executed, as the specificed "vows" do not allow the execve system call. The
process will be killed.
```c
#include "exile.h"
### System call policies / vows
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, working is being done to enable to quickly isolate individual function calls.
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).
exile_launch() demo
## Status
No release yet, experimental, API is unstable, builds will break on updates of this library.
@ -184,7 +77,7 @@ TODO:
## Requirements
Kernel >=3.17
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, this means that Landlock probably won't be used for now, and exile.h will use a combination of namespaces, bind mounts and chroot as fallbacks.
While mostly transparent to users of this API, kernel >= 5.13 is required to take advantage of Landlock and furthermore it depends on distro-provided kernels being reasonable and enabling it by default. In practise, this means that Landlock probably won't be used for now, and exile.h will use a combination of namespaces, bind mounts and chroot as fallbacks.
## FAQ
@ -194,11 +87,11 @@ While mostly transparent to users of this API, kernel >= 5.13 is required to tak
No.
### It doesn't work on my Debian version!
You can thank a Debian-specific kernel patch for that. Execute
`echo 1 > /proc/sys/kernel/unprivileged_userns_clone` to disable that patch for now.
### It doesn't work on Debian!
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.
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.
### Examples
- looqs: https://gitea.quitesimple.org/crtxcr/looqs

12
exile.c
查看文件

@ -430,7 +430,6 @@ int get_vow_argfilter(long syscall, uint64_t vow_promises, struct sock_filter *f
struct exile_syscall_filter ioctl_filter[] = {
EXILE_SYSCALL_FILTER_LOAD_ARG(1),
{ EXILE_SYSCALL_VOW_IOCTL, EXILE_BPF_NO_MATCH_SET(TIOCSTI), 1 },
{ EXILE_SYSCALL_VOW_IOCTL, EXILE_BPF_RETURN_MATCHING, 1 },
{ EXILE_SYSCALL_VOW_STDIO, EXILE_BPF_MATCH(FIONREAD), 1},
{ EXILE_SYSCALL_VOW_STDIO, EXILE_BPF_MATCH(FIONBIO), 1},
@ -644,7 +643,7 @@ int (exile_append_path_policies)(struct exile_policy *exile_policy, unsigned int
int fd = open(path, O_PATH);
if(fd == -1)
{
EXILE_LOG_ERROR("Failed to open %s: %s\n", path, strerror(errno));
EXILE_LOG_ERROR("Failed to open the specified path: %s\n", strerror(errno));
exile_policy->exile_flags |= EXILE_FLAG_ADD_PATH_POLICY_FAIL;
return -1;
}
@ -852,7 +851,7 @@ static int create_chroot_dirs(const char *chroot_target_path, struct exile_path_
ret = mkpath(path_inside_chroot, 0700, baseisfile);
if(ret < 0)
{
EXILE_LOG_ERROR("Error creating directory structure %s while mounting paths to chroot: %s\n", path_inside_chroot, strerror(errno));
EXILE_LOG_ERROR("Error creating directory structure while mounting paths to chroot. %s\n", strerror(errno));
free(path_inside_chroot);
return ret;
}
@ -1209,12 +1208,9 @@ static unsigned int exile_flags_to_landlock(unsigned int flags, int statmode)
result |= LANDLOCK_ACCESS_FS_WRITE_FILE;
if(S_ISDIR(statmode))
{
result |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
result |= LANDLOCK_ACCESS_FS_REMOVE_FILE;
result |= LANDLOCK_ACCESS_FS_MAKE_DIR;
result |= LANDLOCK_ACCESS_FS_MAKE_FIFO;
result |= LANDLOCK_ACCESS_FS_MAKE_REG;
result |= LANDLOCK_ACCESS_FS_MAKE_SOCK;
result |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
result |= LANDLOCK_ACCESS_FS_MAKE_SYM;
}
}
@ -1351,7 +1347,7 @@ static int check_policy_sanity(struct exile_policy *policy)
{
if(path_policy_needs_landlock(path_policy))
{
EXILE_LOG_ERROR("A path policy (%s) needs landlock, but landlock is not available. Fallback not possible\n", path_policy->path);
EXILE_LOG_ERROR("A path policy needs landlock, but landlock is not available. Fallback not possible\n");
return -1;
}
path_policy = path_policy->next;