比較提交
13 次程式碼提交
769f729dc5
...
master
| 作者 | SHA1 | 日期 | |
|---|---|---|---|
| d94263b55f | |||
| a2e3a8ff14 | |||
| 4cfdead5d0 | |||
| bbc8193ea9 | |||
| c9fdeb4a1d | |||
| 3732524bfa | |||
| 4059c1a093 | |||
| 44b9a17bec | |||
| f662398ac3 | |||
| 7b859d0aed | |||
| 5cd0a36ced | |||
| 618f223491 | |||
| 01c5cbf701 |
21
README.md
21
README.md
@@ -1,7 +1,6 @@
|
||||
# exile.h
|
||||
`exile.h` provides an API for processes on Linux to easily isolate themselves in order
|
||||
to mitigate the effect of exploited vulnerabilities, i. e. when attacker has achieved
|
||||
arbitrary code execution. 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.
|
||||
`exile.h` provides an API for processes on Linux to isolate themselves.
|
||||
exile.h makes it easier 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.
|
||||
|
||||
@@ -136,6 +135,8 @@ No release yet, experimental, API is unstable, builds will break on updates of t
|
||||
|
||||
Currently, it's mainly evolving from the needs of my other projects which use exile.h.
|
||||
|
||||
Furthermore, distro specific decisions make things more complicated for some features of exile.h
|
||||
|
||||
|
||||
### Real-world usage
|
||||
- looqs: https://github.com/quitesimpleorg/looqs
|
||||
@@ -199,7 +200,7 @@ While mostly transparent to users of this API, kernel >= 5.13 is required to tak
|
||||
|
||||
### Does the process need to be privileged to utilize the library?
|
||||
|
||||
No.
|
||||
No. But see below.
|
||||
|
||||
### It doesn't work on my Debian version!
|
||||
You can thank a Debian-specific kernel patch for that. Execute
|
||||
@@ -207,6 +208,18 @@ You can thank a Debian-specific kernel patch for that. Execute
|
||||
|
||||
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.
|
||||
|
||||
### It doesn't work on Ubuntu 24.04!
|
||||
Since Ubuntu 24.04, apparmor disallows unprivileged user namespaces unless an application is whitelisted.
|
||||
As unpriviled user namespaces are an double-edges sword on Linux, this move by Canonical is understandable.
|
||||
That said, it is unfortunate for exile.h, since it nows requires shipping apparmor profiles. It also
|
||||
deviates from upstream Linux...
|
||||
|
||||
However, the situation is not that bad and better than when exile.h was started. "vows" are independent of user namespaces. With landlock,
|
||||
filesystems isolation can be achieved without user namespaces.
|
||||
|
||||
|
||||
|
||||
|
||||
### Why "vows"?
|
||||
pledge() cannot be properly implemented using seccomp. The "vow" concept here may look similiar, and it is, but it's not pledge().
|
||||
|
||||
|
||||
193
exile.c
193
exile.c
@@ -621,10 +621,12 @@ struct exile_policy *exile_init_policy()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
result->drop_caps = 1;
|
||||
result->drop_caps = 0;
|
||||
result->not_dumpable = 1;
|
||||
result->no_new_privs = 1;
|
||||
result->namespace_options = EXILE_UNSHARE_MOUNT | EXILE_UNSHARE_USER;
|
||||
result->namespace_options = EXILE_UNSHARE_AUTOMATIC;
|
||||
result->namespace_uid = 0;
|
||||
result->namespace_gid = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -938,10 +940,15 @@ void exile_free_policy(struct exile_policy *ctxt)
|
||||
}
|
||||
|
||||
/* Enters the specified namespaces */
|
||||
static int enter_namespaces(int namespace_options)
|
||||
static int enter_namespaces(int namespace_options, uid_t namespace_uid, gid_t namespace_gid)
|
||||
{
|
||||
if(namespace_options & EXILE_UNSHARE_USER)
|
||||
{
|
||||
uid_t current_uid = getuid();
|
||||
gid_t current_gid = getgid();
|
||||
|
||||
char buf[1024] = {0};
|
||||
|
||||
int ret = unshare(CLONE_NEWUSER);
|
||||
if(ret == -1)
|
||||
{
|
||||
@@ -949,47 +956,51 @@ static int enter_namespaces(int namespace_options)
|
||||
return ret;
|
||||
}
|
||||
|
||||
uid_t current_uid = getuid();
|
||||
gid_t current_gid = getgid();
|
||||
int fd = open("/proc/self/setgroups", O_WRONLY);
|
||||
if(fd == -1)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to open /proc/self/setgroups for writing\n");
|
||||
return -1;
|
||||
}
|
||||
int writesize = snprintf(buf, sizeof(buf), "deny");
|
||||
int writeret = write(fd, buf, writesize);
|
||||
if(writeret < 0 || writeret < writesize)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to write to /proc/self/setgroups: %i (%s)\n", writeret, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
FILE *fp = fopen("/proc/self/setgroups", "w");
|
||||
if(fp == NULL)
|
||||
fd = open("/proc/self/uid_map", O_WRONLY);
|
||||
if(fd == -1)
|
||||
{
|
||||
EXILE_LOG_ERROR("fopen failed while trying to deny setgroups\n");
|
||||
EXILE_LOG_ERROR("Failed to open /proc/self/uid_map for writing\n");
|
||||
return -1;
|
||||
}
|
||||
if(fprintf(fp, "deny") < 0)
|
||||
writesize = snprintf(buf, sizeof(buf), "%u %u 1\n", namespace_uid, current_uid);
|
||||
writeret = write(fd, buf, writesize);
|
||||
if(writeret < 0 || writeret < writesize)
|
||||
{
|
||||
EXILE_LOG_ERROR("fprintf failed while trying to write setgroups\n");
|
||||
EXILE_LOG_ERROR("Failed to write to /proc/self/uid_map: %i (%s)\n", writeret, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
fclose(fp);
|
||||
close(fd);
|
||||
|
||||
fp = fopen("/proc/self/uid_map", "w");
|
||||
if(fp == NULL)
|
||||
{
|
||||
EXILE_LOG_ERROR("fopen failed while trying to write uid_map\n");
|
||||
return -1;
|
||||
}
|
||||
if(fprintf(fp, "0 %i", current_uid) < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("fprintf failed while trying to write uid_map\n");
|
||||
return -1;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
fp = fopen("/proc/self/gid_map", "w");
|
||||
if(fp == NULL)
|
||||
fd = open("/proc/self/gid_map", O_WRONLY);
|
||||
if(fd == -1)
|
||||
{
|
||||
EXILE_LOG_ERROR("fopen failed while trying to write gid_map\n");
|
||||
EXILE_LOG_ERROR("Failed to open /proc/self/gid_map for writing\n");
|
||||
return -1;
|
||||
}
|
||||
if(fprintf(fp, "0 %i", current_gid) < 0)
|
||||
writesize = snprintf(buf, sizeof(buf), "%u %u 1\n", namespace_gid, current_gid);
|
||||
writeret = write(fd, buf, writesize);
|
||||
if(writeret < 0 || writeret < writesize)
|
||||
{
|
||||
EXILE_LOG_ERROR("fprintf failed while trying to write gid_map\n");
|
||||
EXILE_LOG_ERROR("Failed to write to /proc/self/gid_map: %i (%s)\n", writeret, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
fclose(fp);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
if(namespace_options & EXILE_UNSHARE_MOUNT)
|
||||
@@ -1218,6 +1229,9 @@ static unsigned int exile_flags_to_landlock(unsigned int flags, int statmode)
|
||||
if(flags & EXILE_FS_ALLOW_ALL_WRITE)
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_WRITE_FILE;
|
||||
#ifdef LANDLOCK_ACCESS_FS_TRUNCATE
|
||||
result |= LANDLOCK_ACCESS_FS_TRUNCATE;
|
||||
#endif
|
||||
if(S_ISDIR(statmode))
|
||||
{
|
||||
result |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
|
||||
@@ -1227,6 +1241,9 @@ static unsigned int exile_flags_to_landlock(unsigned int flags, int statmode)
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_REG;
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_SOCK;
|
||||
result |= LANDLOCK_ACCESS_FS_MAKE_SYM;
|
||||
#ifdef LANDLOCK_ACCESS_FS_REFER
|
||||
result |= LANDLOCK_ACCESS_FS_REFER;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if(flags & EXILE_FS_ALLOW_EXEC)
|
||||
@@ -1293,15 +1310,42 @@ static unsigned int exile_flags_to_landlock(unsigned int flags, int statmode)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Sets maximum values for the handled access fs... */
|
||||
static int landlock_set_max_handled_access(struct landlock_ruleset_attr *ruleset)
|
||||
{
|
||||
int abi = landlock_create_ruleset(NULL, 0,
|
||||
LANDLOCK_CREATE_RULESET_VERSION);
|
||||
if(abi < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Can't determine landlock ABI version\n");
|
||||
return -1;
|
||||
}
|
||||
ruleset->handled_access_net = 0;
|
||||
if(abi == 1)
|
||||
{
|
||||
ruleset->handled_access_fs = ((LANDLOCK_ACCESS_FS_MAKE_SYM << 1) - 1);
|
||||
}
|
||||
if(abi == 2)
|
||||
{
|
||||
ruleset->handled_access_fs = ((LANDLOCK_ACCESS_FS_REFER << 1) - 1);
|
||||
}
|
||||
if(abi >= 3)
|
||||
{
|
||||
ruleset->handled_access_fs = ((LANDLOCK_ACCESS_FS_TRUNCATE << 1) - 1);
|
||||
/* TODO: think about net */
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int landlock_prepare_ruleset(struct exile_path_policy *policies)
|
||||
{
|
||||
int ruleset_fd = -1;
|
||||
struct landlock_ruleset_attr ruleset_attr;
|
||||
/* We here want the maximum possible ruleset, so set the var to the max possible bitmask.
|
||||
Stolen/Adapted from: [linux src]/security/landlock/limits.h
|
||||
*/
|
||||
ruleset_attr.handled_access_fs = ((LANDLOCK_ACCESS_FS_MAKE_SYM << 1) - 1);
|
||||
|
||||
struct landlock_ruleset_attr ruleset_attr = {0};
|
||||
if(landlock_set_max_handled_access(&ruleset_attr) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
|
||||
if (ruleset_fd < 0)
|
||||
{
|
||||
@@ -1311,7 +1355,7 @@ static int landlock_prepare_ruleset(struct exile_path_policy *policies)
|
||||
struct exile_path_policy *policy = policies;
|
||||
while(policy != NULL)
|
||||
{
|
||||
struct landlock_path_beneath_attr path_beneath;
|
||||
struct landlock_path_beneath_attr path_beneath = {0};
|
||||
path_beneath.parent_fd = open(policy->path, O_PATH | O_CLOEXEC);
|
||||
if(path_beneath.parent_fd < 0)
|
||||
{
|
||||
@@ -1328,6 +1372,13 @@ static int landlock_prepare_ruleset(struct exile_path_policy *policies)
|
||||
return ret;
|
||||
}
|
||||
path_beneath.allowed_access = exile_flags_to_landlock(policy->policy, sb.st_mode);
|
||||
|
||||
/* Required, so the .allowed_access fits .handled_access_fs of the ruleset.
|
||||
* Needed for backwards compatibility, e. g. new binary compiled with new headers,
|
||||
executed on a kernel with an older ABI version which does not have some constant defined...
|
||||
*/
|
||||
path_beneath.allowed_access &= ruleset_attr.handled_access_fs;
|
||||
|
||||
ret = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0);
|
||||
if(ret)
|
||||
{
|
||||
@@ -1481,6 +1532,30 @@ static int enable_no_fs(struct exile_policy *policy)
|
||||
{
|
||||
close_file_fds();
|
||||
|
||||
if(exile_landlock_is_available())
|
||||
{
|
||||
struct landlock_ruleset_attr ruleset_attr = {0};
|
||||
if(landlock_set_max_handled_access(&ruleset_attr) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
int ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
|
||||
if (ruleset_fd < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to create landlock ruleset\n");
|
||||
return -1;
|
||||
}
|
||||
int ret = landlock_restrict_self(ruleset_fd, 0);
|
||||
if(ret != 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to enable no_fs with landlock: %s\n", strerror(errno));
|
||||
close(ruleset_fd);
|
||||
return -1;
|
||||
}
|
||||
close(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(chdir("/proc/self/fdinfo") != 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to change to safe directory: %s\n", strerror(errno));
|
||||
@@ -1532,7 +1607,7 @@ int exile_enable_policy(struct exile_policy *policy)
|
||||
close_file_fds();
|
||||
}
|
||||
|
||||
if(enter_namespaces(policy->namespace_options) < 0)
|
||||
if(enter_namespaces(policy->namespace_options, policy->namespace_uid, policy->namespace_gid) < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Error while trying to enter namespaces\n");
|
||||
return -1;
|
||||
@@ -1615,14 +1690,6 @@ int exile_enable_policy(struct exile_policy *policy)
|
||||
}
|
||||
#endif
|
||||
|
||||
if(policy->no_fs)
|
||||
{
|
||||
if(enable_no_fs(policy) != 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to take away filesystem access of process\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(policy->no_new_fds)
|
||||
{
|
||||
@@ -1634,15 +1701,6 @@ int exile_enable_policy(struct exile_policy *policy)
|
||||
}
|
||||
}
|
||||
|
||||
if(policy->drop_caps)
|
||||
{
|
||||
if(drop_caps() < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("failed to drop capabilities\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(policy->not_dumpable)
|
||||
{
|
||||
if(prctl(PR_SET_DUMPABLE, 0) == -1)
|
||||
@@ -1661,6 +1719,15 @@ int exile_enable_policy(struct exile_policy *policy)
|
||||
}
|
||||
}
|
||||
|
||||
if(policy->no_fs)
|
||||
{
|
||||
if(enable_no_fs(policy) != 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("Failed to take away filesystem access of process\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_LANDLOCK == 1
|
||||
if (can_use_landlock && policy->path_policies != NULL && landlock_restrict_self(landlock_ruleset_fd, 0) != 0)
|
||||
{
|
||||
@@ -1681,12 +1748,19 @@ int exile_enable_policy(struct exile_policy *policy)
|
||||
}
|
||||
}
|
||||
|
||||
if(policy->drop_caps)
|
||||
{
|
||||
if(drop_caps() < 0)
|
||||
{
|
||||
EXILE_LOG_ERROR("failed to drop capabilities\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(policy->syscall_policies != NULL)
|
||||
{
|
||||
return exile_enable_syscall_policy(policy);
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1879,13 +1953,6 @@ char *exile_launch_get(struct exile_launch_params *launch_params, size_t *n)
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
3
exile.h
3
exile.h
@@ -375,6 +375,9 @@ struct exile_policy
|
||||
|
||||
uint64_t vow_promises;
|
||||
|
||||
uid_t namespace_uid;
|
||||
gid_t namespace_gid;
|
||||
|
||||
/* Do not manually add policies here, use exile_append_path_policies() */
|
||||
struct exile_path_policy *path_policies;
|
||||
struct exile_path_policy **path_policies_tail;
|
||||
|
||||
148
test.c
148
test.c
@@ -618,9 +618,9 @@ int test_launch_get()
|
||||
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))
|
||||
if(n != len)
|
||||
{
|
||||
LOG("Lenght does does not match: %lu vs %u\n", n, len);
|
||||
LOG("Lenght does not match: %lu vs %u\n", n, len);
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(content, LAUNCH_GET_TEST_STR) != 0)
|
||||
@@ -661,6 +661,146 @@ int test_clone3_nosys()
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_test_nsuidmap(const char *path, const char *firstfield, const char *secondfield, const char *thirdfield)
|
||||
{
|
||||
char *line = NULL;
|
||||
size_t n = 0;
|
||||
FILE *fp = fopen(path, "r");
|
||||
|
||||
int ret = getdelim(&line, &n, ' ', fp);
|
||||
while(ret != -1 && strlen(line) == 1 && *line == ' ')
|
||||
ret = getdelim(&line, &n, ' ', fp);
|
||||
if(ret == -1)
|
||||
{
|
||||
LOG("getdelim() failed to read a line from %s\n", path);
|
||||
return 1;
|
||||
}
|
||||
line[ret-1] = '\0';
|
||||
if(strcmp(line, firstfield) != 0)
|
||||
{
|
||||
LOG("Invalid value for first entry in map: Expected: %s, was: %s\n", firstfield, line);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = getdelim(&line, &n, ' ', fp);
|
||||
while(ret != -1 && strlen(line) == 1 && *line == ' ')
|
||||
ret = getdelim(&line, &n, ' ', fp);
|
||||
if(ret == -1)
|
||||
{
|
||||
LOG("getdelim() failed to read a line from map\n");
|
||||
return 1;
|
||||
}
|
||||
line[ret-1] = '\0';
|
||||
|
||||
if(strcmp(line, secondfield) != 0)
|
||||
{
|
||||
LOG("Invalid value for second entry in map: Expected: %s, was: %s\n", secondfield, line);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ret = getdelim(&line, &n, ' ', fp);
|
||||
while(ret != -1 && strlen(line) == 1 && *line == ' ')
|
||||
ret = getdelim(&line, &n, ' ', fp);
|
||||
if(ret == -1)
|
||||
{
|
||||
LOG("getdelim() failed to read a line from uid_map\n");
|
||||
return 1;
|
||||
}
|
||||
line[ret-1] = '\0';
|
||||
if(strcmp(line, thirdfield) != 0)
|
||||
{
|
||||
LOG("Invalid value for second entry in map: Expected: %s, was: %s\n", thirdfield, line);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_unshare_user()
|
||||
{
|
||||
char uidstr[64];
|
||||
snprintf(uidstr, sizeof(uidstr), "%u", getuid());
|
||||
|
||||
char gidstr[64];
|
||||
snprintf(gidstr, sizeof(gidstr), "%u", getgid());
|
||||
|
||||
struct exile_policy *policy = exile_init_policy();
|
||||
policy->namespace_options = EXILE_UNSHARE_USER;
|
||||
xexile_enable_policy(policy);
|
||||
|
||||
if(do_test_nsuidmap("/proc/self/uid_map", "0", uidstr, "1") != 0)
|
||||
{
|
||||
LOG("/proc/self/uid_map failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(do_test_nsuidmap("/proc/self/gid_map", "0", gidstr, "1") != 0)
|
||||
{
|
||||
LOG("/proc/self/gid_map failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE *fp = fopen("/proc/self/setgroups", "r");
|
||||
|
||||
char buffer[4096] = { 0 };
|
||||
fread(buffer, sizeof(buffer), 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
if(strcmp(buffer, "deny\n") != 0)
|
||||
{
|
||||
LOG("/proc/self/setgroups does not contain 'deny'\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_unshare_user_own_uid()
|
||||
{
|
||||
uid_t uid = getuid();
|
||||
gid_t gid = getgid();
|
||||
|
||||
char uidstr[64];
|
||||
snprintf(uidstr, sizeof(uidstr), "%u", uid);
|
||||
|
||||
char gidstr[64];
|
||||
snprintf(gidstr, sizeof(gidstr), "%u", gid);
|
||||
|
||||
struct exile_policy *policy = exile_init_policy();
|
||||
policy->namespace_options = EXILE_UNSHARE_USER;
|
||||
policy->namespace_gid = gid;
|
||||
policy->namespace_uid = uid;
|
||||
xexile_enable_policy(policy);
|
||||
|
||||
if(do_test_nsuidmap("/proc/self/uid_map", uidstr, uidstr, "1") != 0)
|
||||
{
|
||||
LOG("/proc/self/uid_map failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(do_test_nsuidmap("/proc/self/gid_map", gidstr, gidstr, "1") != 0)
|
||||
{
|
||||
LOG("/proc/self/gid_map failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE *fp = fopen("/proc/self/setgroups", "r");
|
||||
|
||||
char buffer[4096] = { 0 };
|
||||
fread(buffer, sizeof(buffer), 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
if(strcmp(buffer, "deny\n") != 0)
|
||||
{
|
||||
LOG("/proc/self/setgroups does not contain 'deny'\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dispatcher
|
||||
{
|
||||
char *name;
|
||||
@@ -689,6 +829,10 @@ struct dispatcher dispatchers[] = {
|
||||
{ "launch-get", &test_launch_get},
|
||||
{ "vow_from_str", &test_vows_from_str},
|
||||
{ "clone3_nosys", &test_clone3_nosys},
|
||||
{ "unshare-user", &test_unshare_user},
|
||||
{ "unshare-user-own-uid", &test_unshare_user_own_uid},
|
||||
|
||||
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
||||
25
test.cpp
25
test.cpp
@@ -1,6 +1,7 @@
|
||||
#include "exile.hpp"
|
||||
#include "assert.h"
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
std::string sandboxed_reverse(std::string str)
|
||||
{
|
||||
@@ -35,26 +36,34 @@ int test_exile_launch_stdstring()
|
||||
|
||||
struct not_trivially_copyable
|
||||
{
|
||||
public:
|
||||
public:
|
||||
std::string somecontent;
|
||||
};
|
||||
|
||||
int test_exile_launch_serializer()
|
||||
{
|
||||
static_assert(! std::is_trivially_copyable_v<not_trivially_copyable>);
|
||||
static_assert(!std::is_trivially_copyable_v<not_trivially_copyable>);
|
||||
|
||||
auto serializer = [](const not_trivially_copyable &obj, char *buf, size_t n){
|
||||
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) {
|
||||
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;});
|
||||
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;
|
||||
@@ -68,9 +77,9 @@ int main(int argc, char *argv[])
|
||||
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 },
|
||||
{"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];
|
||||
|
||||
23
test.sh
23
test.sh
@@ -8,41 +8,41 @@ COUNT_SUCCEEDED=0
|
||||
COUNT_FAILED=0
|
||||
COUNT_SKIPPED=0
|
||||
|
||||
function print_fail()
|
||||
print_fail()
|
||||
{
|
||||
echo -e "${RED}$@${NC}" 1>&2
|
||||
printf "${RED}$@${NC}\n" 1>&2
|
||||
}
|
||||
|
||||
function print_success()
|
||||
print_success()
|
||||
{
|
||||
echo -e "${GREEN}$@${NC}"
|
||||
printf "${GREEN}$@${NC}\n"
|
||||
}
|
||||
|
||||
function print_skipped()
|
||||
print_skipped()
|
||||
{
|
||||
echo -e "${YELLOW}$@${NC}"
|
||||
printf "${YELLOW}$@${NC}\n"
|
||||
}
|
||||
|
||||
function runtest_fail()
|
||||
runtest_fail()
|
||||
{
|
||||
print_fail "failed"
|
||||
COUNT_FAILED=$(($COUNT_FAILED+1))
|
||||
}
|
||||
|
||||
function runtest_success()
|
||||
runtest_success()
|
||||
{
|
||||
print_success "ok"
|
||||
COUNT_SUCCEEDED=$((COUNT_SUCCEEDED+1))
|
||||
}
|
||||
|
||||
function runtest_skipped()
|
||||
runtest_skipped()
|
||||
{
|
||||
print_skipped "skipped"
|
||||
COUNT_SKIPPED=$((COUNT_SKIPPED+1))
|
||||
}
|
||||
|
||||
|
||||
function runtest()
|
||||
runtest()
|
||||
{
|
||||
testbin="$1"
|
||||
testname="$2"
|
||||
@@ -52,7 +52,8 @@ function runtest()
|
||||
|
||||
echo -n "Running $testname... "
|
||||
#exit $? to suppress shell message like "./test.sh: line 18: pid Bad system call"
|
||||
(./$testbin "$testname" || exit $?) &>> "${test_log_file}"
|
||||
(./$testbin "$testname" || exit $?) >> "${test_log_file}" 2>&1
|
||||
|
||||
ret=$?
|
||||
SUCCESS="no"
|
||||
if [ $ret -eq 0 ] ; then
|
||||
|
||||
新增問題並參考
封鎖使用者