Сравнить коммиты
Нет общих коммитов. «4a3ac8e0bc1fe4df353752fc5ed2386a8a0c6013» и «4824c6eaa9043878daaba7b3778338f5bf913f06» имеют совершенно разные истории.
4a3ac8e0bc
...
4824c6eaa9
88
README.md
88
README.md
@ -1,83 +1,27 @@
|
||||
# exile.h
|
||||
`exile.h` is a header-only library, enabling processes to easily isolate themselves on Linux for exploit mitigation. exile.h wants to make existing technologies, such as Seccomp and Linux Namespaces, easier to use. Those generally
|
||||
require knowledge of details and are not trivial for developers to employ, which prevents a more widespread adoption.
|
||||
|
||||
The following section gives small quick examples. Then the motivation is explained in more detail.
|
||||
Proper API documentation will be maintained in other files.
|
||||
|
||||
## Quick demo
|
||||
TODO This section will demonstrate the simplicity of the API, but only serves as an overview.
|
||||
|
||||
### Filesystem isolation
|
||||
|
||||
|
||||
|
||||
### System call policies / vows
|
||||
|
||||
|
||||
### Isolation of single functions
|
||||
exile_launch() demo
|
||||
`exile.h` is a simple header-only library that provides an interface to isolate 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.
|
||||
|
||||
## Status
|
||||
No release yet, experimental, API is unstable, builds will break on updates of this library.
|
||||
No release yet, expiremental, API is unstable, builds will break on updates of this library.
|
||||
|
||||
Currently, it's mainly evolving from the needs of my other projects.
|
||||
|
||||
## Motivation and Background
|
||||
exile.h unlocks existing Linux mechanisms to facilite 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 to restrict 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.
|
||||
|
||||
## Example: Archive extraction
|
||||
A programming uncompressing archives does not need network access, but should a bug allow code execution, obviously the payload may also access the network. Once the target path is known, it doesn't need access to the whole file system, only write-permissions to the target directory and read on the archive file(s).
|
||||
|
||||
TODO example with exile.h applied on "tar" or "unzip". Link to repo.
|
||||
|
||||
## Example: Web apps
|
||||
Those generally don't need access to the whole filesystem hierarchy, nor do they necessarily require the ability to execute other processes.
|
||||
|
||||
Way more examples can be given, but we can put it in simple words: A general purpose OS allow a process to do more things than it actually needs to do.
|
||||
Currently, it's mainly evolving according to the needs of my other projects.
|
||||
|
||||
## 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, removing the need to specifc rules for syscalls.
|
||||
- Dropping privileges in general, such as capabilities
|
||||
- Isolating the application from the network, etc. through Namespaces
|
||||
- Helpers to isolate single functions
|
||||
|
||||
|
||||
## What it's not
|
||||
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, like web browsers do. This allows a more fine-grained approach, as the developers
|
||||
is more familiar with the software. Applying restrictions with solutions like AppArmor requires
|
||||
them to be present 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.
|
||||
- Systemcall filtering (using seccomp-bpf)
|
||||
- restricting file system access (using Landlock and/or Namespaces)
|
||||
- dropping privileges
|
||||
- isolating the application from the network, etc.
|
||||
|
||||
## Requirements
|
||||
|
||||
Kernel >=3.17
|
||||
|
||||
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.
|
||||
``sys/capabilities.h`` header. Depending on your distribution, libcap
|
||||
might be needed for this.
|
||||
|
||||
While mostly transparent to users of this API, kernel >= 5.13 is required to take advantage of Landlock.
|
||||
|
||||
|
||||
|
||||
## FAQ
|
||||
@ -85,7 +29,7 @@ While mostly transparent to users of this API, kernel >= 5.13 is required to tak
|
||||
|
||||
### Does the process need to be priviliged to utilize the library?
|
||||
|
||||
No.
|
||||
No.
|
||||
|
||||
### It doesn't work on Debian!
|
||||
|
||||
@ -96,15 +40,13 @@ the library may check against that. Execute
|
||||
### Examples
|
||||
- looqs: https://gitea.quitesimple.org/crtxcr/looqs
|
||||
- qswiki: https://gitea.quitesimple.org/crtxcr/qswiki
|
||||
|
||||
Outdated:
|
||||
- cgit sandboxed: https://gitea.quitesimple.org/crtxcr/cgitsb
|
||||
- qpdfviewsb sandboxed (quick and dirty): https://gitea.quitesimple.org/crtxcr/qpdfviewsb
|
||||
|
||||
|
||||
### Contributing
|
||||
|
||||
Contributions are very welcome. Options:
|
||||
Contributions are very welcome. Options:
|
||||
|
||||
1. Pull-Request on [github](https://github.com/quitesimpleorg/exile.h)
|
||||
2. Mail to `exile at quitesimple.org` with instructions on where to pull the changes from.
|
||||
|
490
exile.h
490
exile.h
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 Albert Schwarzkopf <mail at quitesimple dot org>
|
||||
* Copyright (c) 2021 Albert S. <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
|
||||
@ -77,7 +77,7 @@
|
||||
#define EXILE_UNSHARE_MOUNT 1<<3
|
||||
|
||||
#ifndef EXILE_LOG_ERROR
|
||||
#define EXILE_LOG_ERROR(...) do { fprintf(stderr, "exile.h: %s(): Error: ", __func__); fprintf(stderr, __VA_ARGS__); } while(0)
|
||||
#define EXILE_LOG_ERROR(...) fprintf(stderr, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef EXILE_TEMP_DIR
|
||||
@ -328,11 +328,6 @@ is thus up to the default policy */
|
||||
|
||||
|
||||
#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];
|
||||
@ -361,7 +356,7 @@ struct exile_policy
|
||||
|
||||
uint64_t vow_promises;
|
||||
|
||||
/* Do not manually add policies here, use exile_append_path_policies() */
|
||||
/* Do not manually add policies here, use exile_append_path_polic*() */
|
||||
struct exile_path_policy *path_policies;
|
||||
struct exile_path_policy **path_policies_tail;
|
||||
|
||||
@ -369,7 +364,6 @@ struct exile_policy
|
||||
struct exile_syscall_policy *syscall_policies;
|
||||
struct exile_syscall_policy **syscall_policies_tail;
|
||||
|
||||
uint32_t exile_flags;
|
||||
};
|
||||
|
||||
|
||||
@ -524,7 +518,6 @@ static struct syscall_vow_map exile_vow_map[] =
|
||||
{EXILE_SYS(munlockall), EXILE_SYSCALL_VOW_STDIO},
|
||||
{EXILE_SYS(vhangup), EXILE_SYSCALL_VOW_STDIO},
|
||||
{EXILE_SYS(prctl), EXILE_SYSCALL_VOW_STDIO|EXILE_SYSCALL_VOW_PRCTL|EXILE_SYSCALL_VOW_SECCOMP_INSTALL},
|
||||
{EXILE_SYS(arch_prctl), EXILE_SYSCALL_VOW_STDIO|EXILE_SYSCALL_VOW_PRCTL},
|
||||
{EXILE_SYS(setrlimit), EXILE_SYSCALL_VOW_PROC},
|
||||
{EXILE_SYS(sync), EXILE_SYSCALL_VOW_STDIO},
|
||||
{EXILE_SYS(gettid), EXILE_SYSCALL_VOW_STDIO},
|
||||
@ -623,7 +616,6 @@ static struct syscall_vow_map exile_vow_map[] =
|
||||
{EXILE_SYS(recvmmsg), EXILE_SYSCALL_VOW_STDIO},
|
||||
{EXILE_SYS(fanotify_init), EXILE_SYSCALL_VOW_FSNOTIFY},
|
||||
{EXILE_SYS(fanotify_mark), EXILE_SYSCALL_VOW_FSNOTIFY},
|
||||
{EXILE_SYS(prlimit64), EXILE_SYSCALL_VOW_STDIO},
|
||||
{EXILE_SYS(open_by_handle_at), EXILE_SYSCALL_VOW_RPATH},
|
||||
{EXILE_SYS(sendmmsg), EXILE_SYSCALL_VOW_STDIO},
|
||||
{EXILE_SYS(getcpu), EXILE_SYSCALL_VOW_STDIO},
|
||||
@ -668,7 +660,6 @@ int exile_append_syscall_policy(struct exile_policy *exile_policy, long syscall,
|
||||
if(newpolicy == NULL)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to allocate memory for syscall policy\n");
|
||||
exile_policy->exile_flags |= EXILE_FLAG_ADD_SYSCALL_POLICY_FAIL;
|
||||
return -1;
|
||||
}
|
||||
newpolicy->policy = syscall_policy;
|
||||
@ -677,7 +668,6 @@ int exile_append_syscall_policy(struct exile_policy *exile_policy, long syscall,
|
||||
if(n > EXILE_ARGFILTERS_COUNT)
|
||||
{
|
||||
EXILE_LOG_ERROR("Too many argfilters supplied\n");
|
||||
exile_policy->exile_flags |= EXILE_FLAG_ADD_SYSCALL_POLICY_FAIL;
|
||||
return -1;
|
||||
}
|
||||
for(size_t i = 0; i < n; i++)
|
||||
@ -931,7 +921,6 @@ struct exile_policy *exile_init_policy()
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* Appends path policies to the exile_policy object
|
||||
* The last paramater must be NULL
|
||||
*
|
||||
@ -948,19 +937,10 @@ int exile_append_path_policies(struct exile_policy *exile_policy, unsigned int p
|
||||
path = va_arg(args, char*);
|
||||
while(path != NULL)
|
||||
{
|
||||
int fd = open(path, O_PATH);
|
||||
if(fd == -1)
|
||||
{
|
||||
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;
|
||||
}
|
||||
close(fd);
|
||||
struct exile_path_policy *newpolicy = (struct exile_path_policy *) calloc(1, sizeof(struct exile_path_policy));
|
||||
if(newpolicy == NULL)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to allocate memory for path policy\n");
|
||||
exile_policy->exile_flags |= EXILE_FLAG_ADD_PATH_POLICY_FAIL;
|
||||
return -1;
|
||||
}
|
||||
newpolicy->path = path;
|
||||
@ -977,7 +957,12 @@ int exile_append_path_policies(struct exile_policy *exile_policy, unsigned int p
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define exile_append_path_policies(e, p, ...) exile_append_path_policies(e, p, __VA_ARGS__, NULL)
|
||||
int exile_append_path_policy(struct exile_policy *exile_policy, unsigned int path_policy, const char *path)
|
||||
{
|
||||
return exile_append_path_policies(exile_policy, path_policy, path, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Fills buffer with random characters a-z.
|
||||
@ -1003,26 +988,28 @@ int random_string(char *buffer, size_t buffer_length)
|
||||
}
|
||||
|
||||
|
||||
/* Creates a directory/file and all necessary parent directories
|
||||
* @returns: 0 on success, -ERRNO on failure
|
||||
*/
|
||||
static int mkpath(const char *p, mode_t mode, int baseisfile)
|
||||
/* Creates a directory and all necessary parent directories
|
||||
*
|
||||
* @returns: 0 on success, -ERRNO on failure
|
||||
* */
|
||||
static int mkdir_structure(const char *p, mode_t mode)
|
||||
{
|
||||
char path[PATH_MAX + 1] = {0};
|
||||
int ret = snprintf(path, sizeof(path), "%s%c", p, (baseisfile) ? '\0' : '/');
|
||||
if(ret < 0)
|
||||
char path[PATH_MAX] = { 0 };
|
||||
int res = snprintf(path, sizeof(path), "%s/", p);
|
||||
if(res < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("error during path concatination\n");
|
||||
EXILE_LOG_ERROR("exile: mkdir_strucutre: error during path concatination\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if((size_t)ret >= sizeof(path))
|
||||
if(res >= PATH_MAX)
|
||||
{
|
||||
EXILE_LOG_ERROR("path concatination truncated\n");
|
||||
EXILE_LOG_ERROR("exile: mkdir_structure: path concatination truncated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
char *begin = path;
|
||||
char *end = begin + 1;
|
||||
char *end = begin+1;
|
||||
|
||||
while(*end)
|
||||
{
|
||||
@ -1031,13 +1018,18 @@ static int mkpath(const char *p, mode_t mode, int baseisfile)
|
||||
*end = 0;
|
||||
if(mkdir(begin, mode) < 0)
|
||||
{
|
||||
if(errno != EEXIST)
|
||||
if(errno == EEXIST)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to create directory: %s\n", begin);
|
||||
//TODO: stat, test if it is a directory, if not, err
|
||||
}
|
||||
else
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to create directory for chroot: %s\n", begin);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*end = '/';
|
||||
++end;
|
||||
while(*end == '/')
|
||||
{
|
||||
++end;
|
||||
@ -1048,17 +1040,6 @@ static int mkpath(const char *p, mode_t mode, int baseisfile)
|
||||
++end;
|
||||
}
|
||||
}
|
||||
if(baseisfile)
|
||||
{
|
||||
ret = creat(p, mode);
|
||||
if(ret == -1)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to create file: %s\n", begin);
|
||||
return ret;
|
||||
}
|
||||
close(ret);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1107,92 +1088,46 @@ static int path_policy_needs_landlock(struct exile_path_policy *path_policy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: we can do va_args */
|
||||
char *concat_path(const char *first, const char *second)
|
||||
{
|
||||
char *result = (char *) calloc(1, PATH_MAX);
|
||||
if(result == NULL)
|
||||
{
|
||||
EXILE_LOG_ERROR("calloc failed\n");
|
||||
return NULL;
|
||||
}
|
||||
//TODO: We can strip multiple redundant slashes
|
||||
int written = snprintf(result, PATH_MAX, "%s/%s", first, second);
|
||||
if(written < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Error during path concatination\n");
|
||||
return NULL;
|
||||
}
|
||||
if(written >= PATH_MAX)
|
||||
{
|
||||
EXILE_LOG_ERROR("path concatination truncated\n");
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/* Helper to mount directories into the chroot path "chroot_target_path"
|
||||
* Paths will be created if necessary
|
||||
|
||||
|
||||
/* Creates the file system hierarchy for the chroot
|
||||
* @returns: 0 on sucess, -ERRNO on failure */
|
||||
static int create_chroot_dirs(const char *chroot_target_path, struct exile_path_policy *path_policy)
|
||||
static int mount_to_chroot(const char *chroot_target_path, struct exile_path_policy *path_policy)
|
||||
{
|
||||
while(path_policy != NULL)
|
||||
{
|
||||
struct stat sb;
|
||||
int ret = stat(path_policy->path, &sb);
|
||||
if(ret < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("stat failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
int baseisfile = 0;
|
||||
if(S_ISREG(sb.st_mode))
|
||||
char path_inside_chroot[PATH_MAX];
|
||||
int written = snprintf(path_inside_chroot, sizeof(path_inside_chroot), "%s/%s", chroot_target_path, path_policy->path);
|
||||
if(written < 0)
|
||||
{
|
||||
baseisfile = 1;
|
||||
EXILE_LOG_ERROR("exile: mount_to_chroot: Error during path concatination\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
char *path_inside_chroot = concat_path(chroot_target_path, path_policy->path);
|
||||
if(path_inside_chroot == NULL)
|
||||
if(written >= PATH_MAX)
|
||||
{
|
||||
return 1;
|
||||
EXILE_LOG_ERROR("exile: mount_to_chroot: path concatination truncated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mkpath(path_inside_chroot, 0700, baseisfile);
|
||||
int ret = mkdir_structure(path_inside_chroot, 0700);
|
||||
if(ret < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Error creating directory structure while mounting paths to chroot. %s\n", strerror(errno));
|
||||
free(path_inside_chroot);
|
||||
return ret;
|
||||
}
|
||||
path_policy = path_policy->next;
|
||||
free(path_inside_chroot);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perform_mounts(const char *chroot_target_path, struct exile_path_policy *path_policy)
|
||||
{
|
||||
while(path_policy != NULL)
|
||||
{
|
||||
int mount_flags = get_policy_mount_flags(path_policy);
|
||||
|
||||
char *path_inside_chroot = concat_path(chroot_target_path, path_policy->path);
|
||||
if(path_inside_chroot == NULL)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
//all we do is bind mounts
|
||||
mount_flags |= MS_BIND;
|
||||
|
||||
|
||||
if(path_policy->policy & EXILE_FS_ALLOW_ALL_READ || path_policy->policy & EXILE_FS_ALLOW_ALL_WRITE)
|
||||
{
|
||||
int ret = mount(path_policy->path, path_inside_chroot, NULL, mount_flags, NULL);
|
||||
ret = mount(path_policy->path, path_inside_chroot, NULL, mount_flags, NULL);
|
||||
if(ret < 0 )
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to mount %s to %s: %s\n", path_policy->path, path_inside_chroot, strerror(errno));
|
||||
free(path_inside_chroot);
|
||||
EXILE_LOG_ERROR("Error: Failed to mount %s to %s: %s\n", path_policy->path, path_inside_chroot, strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1200,19 +1135,16 @@ static int perform_mounts(const char *chroot_target_path, struct exile_path_poli
|
||||
ret = mount(NULL, path_inside_chroot, NULL, mount_flags | MS_REMOUNT, NULL);
|
||||
if(ret < 0 )
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to remount %s: %s\n", path_inside_chroot, strerror(errno));
|
||||
free(path_inside_chroot);
|
||||
EXILE_LOG_ERROR("Error: Failed to remount %s: %s\n", path_inside_chroot, strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
path_policy = path_policy->next;
|
||||
free(path_inside_chroot);
|
||||
}
|
||||
path_policy = path_policy->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Frees the memory taken by a exile_policy object
|
||||
*/
|
||||
@ -1247,7 +1179,7 @@ static int enter_namespaces(int namespace_options)
|
||||
int ret = unshare(CLONE_NEWUSER);
|
||||
if(ret == -1)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to unshare user namespaces: %s\n", strerror(errno));
|
||||
EXILE_LOG_ERROR("Error: Failed to unshare user namespaces: %s\n", strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1299,7 +1231,7 @@ static int enter_namespaces(int namespace_options)
|
||||
int ret = unshare(CLONE_NEWNS);
|
||||
if(ret == -1)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to unshare mount namespaces: %s\n", strerror(errno));
|
||||
EXILE_LOG_ERROR("Error: Failed to unshare mount namespaces: %s\n", strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@ -1309,7 +1241,7 @@ static int enter_namespaces(int namespace_options)
|
||||
int ret = unshare(CLONE_NEWNET);
|
||||
if(ret == -1)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to unshare network namespace: %s\n", strerror(errno));
|
||||
EXILE_LOG_ERROR("Error: Failed to unshare network namespace: %s\n", strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@ -1383,7 +1315,7 @@ static void append_syscall_to_bpf(struct exile_syscall_policy *syscallpolicy, st
|
||||
__u8 next_syscall_pc = 1;
|
||||
if(__builtin_add_overflow(next_syscall_pc, syscallpolicy->argfilterscount, &next_syscall_pc))
|
||||
{
|
||||
EXILE_LOG_ERROR("Overflow while trying to calculate jump offset\n");
|
||||
EXILE_LOG_ERROR("Error: Overflow while trying to calculate jump offset\n");
|
||||
/* TODO: Return error */
|
||||
return;
|
||||
}
|
||||
@ -1483,27 +1415,67 @@ static int exile_enable_syscall_policy(struct exile_policy *policy)
|
||||
}
|
||||
|
||||
#if HAVE_LANDLOCK == 1
|
||||
static unsigned int exile_flags_to_landlock(unsigned int flags, int statmode)
|
||||
static unsigned int exile_flags_to_landlock(unsigned int flags)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
if(flags & EXILE_FS_ALLOW_ALL_READ)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_READ_FILE;
|
||||
if(S_ISDIR(statmode))
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_READ_DIR;
|
||||
}
|
||||
result |= LANDLOCK_ACCESS_FS_READ_DIR;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_ALL_WRITE)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_REG;
|
||||
result |= LANDLOCK_ACCESS_FS_WRITE_FILE;
|
||||
if(S_ISDIR(statmode))
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_FILE;
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_REG;
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_SYM;
|
||||
}
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_FILE;
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_SYM;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_DEV)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_BLOCK;
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_CHAR;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_BLOCK)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_BLOCK;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_CHAR)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_CHAR;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_DIR)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_DIR;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_FIFO)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_FIFO;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_REG)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_REG;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_SOCK)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_SOCK;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_SYM)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_SYM;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_REMOVE)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_FILE;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_REMOVE_DIR)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_REMOVE_FILE)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_FILE;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_EXEC)
|
||||
{
|
||||
@ -1513,58 +1485,9 @@ static unsigned int exile_flags_to_landlock(unsigned int flags, int statmode)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_WRITE_FILE;
|
||||
}
|
||||
if(S_ISDIR(statmode))
|
||||
if(flags & EXILE_FS_ALLOW_READ_DIR)
|
||||
{
|
||||
if(flags & EXILE_FS_ALLOW_DEV)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_BLOCK;
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_CHAR;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_BLOCK)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_BLOCK;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_CHAR)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_CHAR;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_DIR)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_DIR;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_FIFO)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_FIFO;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_REG)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_REG;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_SOCK)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_SOCK;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_MAKE_SYM)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_SYM;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_REMOVE)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_FILE;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_REMOVE_DIR)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_REMOVE_FILE)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_FILE;
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_READ_DIR)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_READ_DIR;
|
||||
}
|
||||
result |= LANDLOCK_ACCESS_FS_READ_DIR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -1595,16 +1518,8 @@ static int landlock_prepare_ruleset(struct exile_path_policy *policies)
|
||||
close(ruleset_fd);
|
||||
return path_beneath.parent_fd;
|
||||
}
|
||||
struct stat sb;
|
||||
int ret = fstat(path_beneath.parent_fd, &sb);
|
||||
if(ret)
|
||||
{
|
||||
EXILE_LOG_ERROR("fstat failed %s\n", strerror(errno));
|
||||
close(ruleset_fd);
|
||||
return ret;
|
||||
}
|
||||
path_beneath.allowed_access = exile_flags_to_landlock(policy->policy, sb.st_mode);
|
||||
ret = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0);
|
||||
path_beneath.allowed_access = exile_flags_to_landlock(policy->policy);
|
||||
int ret = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0);
|
||||
if(ret)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to update ruleset while processsing policy path %s\n", policy->path);
|
||||
@ -1638,7 +1553,7 @@ static int check_policy_sanity(struct exile_policy *policy)
|
||||
{
|
||||
if(path_policy_needs_landlock(path_policy))
|
||||
{
|
||||
EXILE_LOG_ERROR("A path policy needs landlock, but landlock is not available. Fallback not possible\n");
|
||||
EXILE_LOG_ERROR("Error: A path policy needs landlock, but landlock is not available. Fallback not possible\n");
|
||||
return -1;
|
||||
}
|
||||
path_policy = path_policy->next;
|
||||
@ -1651,7 +1566,7 @@ static int check_policy_sanity(struct exile_policy *policy)
|
||||
{
|
||||
if(policy->path_policies == NULL)
|
||||
{
|
||||
EXILE_LOG_ERROR("Cannot mount path policies to chroot if none are given\n");
|
||||
EXILE_LOG_ERROR("Cannot mount path policies to chroot if non are given\n");
|
||||
return -1;
|
||||
}
|
||||
if(!(policy->namespace_options & EXILE_UNSHARE_MOUNT))
|
||||
@ -1774,14 +1689,9 @@ static int enable_no_fs(struct exile_policy *policy)
|
||||
*/
|
||||
int exile_enable_policy(struct exile_policy *policy)
|
||||
{
|
||||
if((policy->exile_flags & EXILE_FLAG_ADD_PATH_POLICY_FAIL) || (policy->exile_flags & EXILE_FLAG_ADD_SYSCALL_POLICY_FAIL))
|
||||
{
|
||||
EXILE_LOG_ERROR("At least one syscall or path policy was not successfully added!\n");
|
||||
return -1;
|
||||
}
|
||||
if(check_policy_sanity(policy) != 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Policy sanity check failed. Cannot apply policy!\n");
|
||||
EXILE_LOG_ERROR("Error: Policy sanity check failed. Cannot apply policy!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -1806,12 +1716,12 @@ int exile_enable_policy(struct exile_policy *policy)
|
||||
int res = snprintf(policy->chroot_target_path, sizeof(policy->chroot_target_path), "%s/.sandbox_%" PRIdMAX "_%s", EXILE_TEMP_DIR, (intmax_t)getpid(), random_str);
|
||||
if(res < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("error during path concatination\n");
|
||||
EXILE_LOG_ERROR("exile: exile_enable_policy: error during path concatination\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if(res >= PATH_MAX)
|
||||
{
|
||||
EXILE_LOG_ERROR("path concatination truncated\n");
|
||||
EXILE_LOG_ERROR("exile: exile_enable_policy: path concatination truncated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@ -1822,15 +1732,9 @@ int exile_enable_policy(struct exile_policy *policy)
|
||||
}
|
||||
}
|
||||
|
||||
if(create_chroot_dirs(policy->chroot_target_path, policy->path_policies) < 0)
|
||||
if(mount_to_chroot(policy->chroot_target_path, policy->path_policies) < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("bind mounting of path policies failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(perform_mounts(policy->chroot_target_path, policy->path_policies) < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to remount\n");
|
||||
EXILE_LOG_ERROR("mount_to_chroot: bind mounting of path policies failed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -1839,7 +1743,7 @@ int exile_enable_policy(struct exile_policy *policy)
|
||||
{
|
||||
if(chroot(policy->chroot_target_path) < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("failed to enter %s\n", policy->chroot_target_path);
|
||||
EXILE_LOG_ERROR("chroot: failed to enter %s\n", policy->chroot_target_path);
|
||||
return -1;
|
||||
}
|
||||
const char *chdir_target_path = policy->chdir_path;
|
||||
@ -1862,7 +1766,7 @@ int exile_enable_policy(struct exile_policy *policy)
|
||||
landlock_ruleset_fd = landlock_prepare_ruleset(policy->path_policies);
|
||||
if(landlock_ruleset_fd < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to prepare landlock ruleset: %s\n", strerror(errno));
|
||||
EXILE_LOG_ERROR("landlock_prepare_ruleset: Failed to prepare landlock ruleset: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -1998,165 +1902,3 @@ int exile_vow(uint64_t promises)
|
||||
exile_free_policy(policy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
struct exile_launch_result
|
||||
{
|
||||
int tid;
|
||||
int read_fd;
|
||||
int write_fd;
|
||||
};
|
||||
|
||||
static int child_read_pipe[2];
|
||||
static int child_write_pipe[2];
|
||||
|
||||
static int exile_clone_handle(void *arg)
|
||||
{
|
||||
struct exile_launch_params *params = (struct exile_launch_params *) arg;
|
||||
struct exile_policy *policy = (struct exile_policy *) params->policy;
|
||||
|
||||
int ret = exile_enable_policy(policy);
|
||||
if(ret != 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to enable policy\n");
|
||||
close(child_read_pipe[1]);
|
||||
close(child_write_pipe[0]);
|
||||
return 1;
|
||||
}
|
||||
ret = dup2(child_read_pipe[1], 1);
|
||||
if(ret == -1)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to redirect stdout to pipe\n");
|
||||
return 1;
|
||||
}
|
||||
ret = params->func(params->funcarg);
|
||||
fclose(stdout);
|
||||
close(child_read_pipe[1]);
|
||||
close(child_write_pipe[0]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
int ret = pipe(child_read_pipe);
|
||||
if(ret != 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("read pipe creation failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pipe(child_write_pipe);
|
||||
if(ret != 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("write pipe creation failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct rlimit rlimit;
|
||||
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);
|
||||
if(stack == NULL)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to allocate stack memory for child\n");
|
||||
return 1;
|
||||
}
|
||||
stack += size;
|
||||
ret = clone(&exile_clone_handle, stack, 17 /* SIGCHLD */, launch_params);
|
||||
if(ret == -1)
|
||||
{
|
||||
EXILE_LOG_ERROR("clone failed(): %s\n", strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
close(child_read_pipe[1]);
|
||||
close(child_write_pipe[0]);
|
||||
|
||||
launch_result->tid = ret;
|
||||
launch_result->read_fd = child_read_pipe[0];
|
||||
launch_result->write_fd = child_write_pipe[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
*n = 0;
|
||||
struct exile_launch_result launch_result;
|
||||
int launch = exile_launch(launch_params, &launch_result);
|
||||
if(launch < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
char *result = NULL;
|
||||
size_t size = 0;
|
||||
FILE *stream = open_memstream(&result, &size);
|
||||
while(1)
|
||||
{
|
||||
char buffer[4096];
|
||||
int ret = read(launch_result.read_fd, buffer, sizeof(buffer));
|
||||
if(ret == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(ret == -1)
|
||||
{
|
||||
if(errno == EINTR)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
EXILE_LOG_ERROR("Failed to read from read file descriptor\n");
|
||||
close(launch_result.read_fd);
|
||||
fclose(stream);
|
||||
return NULL;
|
||||
}
|
||||
size_t written = fwrite(buffer, 1, ret, stream);
|
||||
if(written != (size_t) ret)
|
||||
{
|
||||
EXILE_LOG_ERROR("Short item write");
|
||||
/* TODO: can we seek and free? */
|
||||
close(launch_result.read_fd);
|
||||
fclose(stream);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
fclose(stream);
|
||||
int seek = fseek(stream, 0, SEEK_SET);
|
||||
if(seek == -1)
|
||||
{
|
||||
EXILE_LOG_ERROR("fseek failed\n");
|
||||
close(launch_result.read_fd);
|
||||
return NULL;
|
||||
}
|
||||
close(launch_result.read_fd);
|
||||
*n = size;
|
||||
return result;
|
||||
}
|
||||
|
148
test.c
148
test.c
@ -404,7 +404,7 @@ int test_landlock()
|
||||
return 1;
|
||||
}
|
||||
struct exile_policy *policy = exile_init_policy();
|
||||
exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ, "/proc/self/fd");
|
||||
exile_append_path_policy(policy, EXILE_FS_ALLOW_ALL_READ, "/proc/self/fd");
|
||||
xexile_enable_policy(policy);
|
||||
|
||||
int fd = open("/", O_RDONLY | O_CLOEXEC);
|
||||
@ -418,7 +418,7 @@ int test_landlock()
|
||||
int test_landlock_deny_write()
|
||||
{
|
||||
struct exile_policy *policy = exile_init_policy();
|
||||
exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ, "/tmp/");
|
||||
exile_append_path_policy(policy, EXILE_FS_ALLOW_ALL_READ, "/tmp/");
|
||||
xexile_enable_policy(policy);
|
||||
|
||||
int fd = open("/tmp/a", O_WRONLY | O_CLOEXEC);
|
||||
@ -487,144 +487,6 @@ int test_no_new_fds()
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
fprintf(stderr, "Failed: mkpath(file) returned: %i\n", ret);
|
||||
return 1;
|
||||
}
|
||||
ret = mkpath(dirpath, 0700, 0);
|
||||
if(ret != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed: mkpath(dirpath) returned: %i\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct stat statbuf;
|
||||
ret = stat(filepath, &statbuf);
|
||||
if(ret != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed: stat on filepath returned: %i\n", ret);
|
||||
return 1;
|
||||
}
|
||||
if(!S_ISREG(statbuf.st_mode))
|
||||
{
|
||||
fprintf(stderr, "Failed: mkpath did not create a file: %i\n", ret);
|
||||
return 1;
|
||||
}
|
||||
ret = stat(dirpath, &statbuf);
|
||||
if(ret != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed: stat on dirpath returned: %i\n", ret);
|
||||
return 1;
|
||||
}
|
||||
if(!S_ISDIR(statbuf.st_mode))
|
||||
{
|
||||
fprintf(stderr, "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;
|
||||
}
|
||||
|
||||
int do_launch_test(void *arg)
|
||||
{
|
||||
int num = *(int *)(arg);
|
||||
num += 1;
|
||||
char buffer[512] = { 0 };
|
||||
read(child_write_pipe[0], 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;
|
||||
int launchfd = exile_launch(¶ms, &res);
|
||||
if(launchfd < 0)
|
||||
{
|
||||
printf("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);
|
||||
printf("Before wait, got: %i\n", s);
|
||||
fflush(stdout);
|
||||
if(strstr(buffer, "Echoing: 1234") == NULL)
|
||||
{
|
||||
printf("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))
|
||||
{
|
||||
printf("Lenght does does not match: %lu vs %u\n", n, len);
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(content, LAUNCH_GET_TEST_STR) != 0)
|
||||
{
|
||||
printf("Received content differs\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dispatcher
|
||||
{
|
||||
char *name;
|
||||
@ -646,11 +508,7 @@ struct dispatcher dispatchers[] = {
|
||||
{ "landlock", &test_landlock},
|
||||
{ "landlock-deny-write", &test_landlock_deny_write },
|
||||
{ "no_fs", &test_nofs},
|
||||
{ "no_new_fds", &test_no_new_fds},
|
||||
{ "mkpath", &test_mkpath},
|
||||
{ "failflags", &test_fail_flags},
|
||||
{ "launch", &test_launch},
|
||||
{ "launch-get", &test_launch_get},
|
||||
{ "no_new_fds", &test_no_new_fds}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
Загрузка…
Ссылка в новой задаче
Block a user