Introduce "no_fs" and "no_new_fd" options.
no_fs is a simple way to take away all FS access, without constructing path_policies etc. no_new_fd disallows opening any new file descriptors
This commit is contained in:
orang tua
57238b535c
melakukan
4a4d551e75
142
qssb.h
142
qssb.h
@ -29,6 +29,8 @@
|
||||
#include <sys/mount.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -158,6 +160,29 @@ static int default_blacklisted_syscals[] = {
|
||||
-1
|
||||
};
|
||||
|
||||
/* TODO: Check for completion
|
||||
* Known blacklisting problem (catch up game, etc.)
|
||||
*
|
||||
* However, we use it to enhance "no_fs" policy, which does not solely rely
|
||||
* on seccomp anyway */
|
||||
static int fs_access_syscalls[] = {
|
||||
QSSB_SYS(chdir),
|
||||
QSSB_SYS(truncate),
|
||||
QSSB_SYS(stat),
|
||||
QSSB_SYS(flock),
|
||||
QSSB_SYS(chmod),
|
||||
QSSB_SYS(chown),
|
||||
QSSB_SYS(setxattr),
|
||||
QSSB_SYS(utime),
|
||||
QSSB_SYS(ioctl),
|
||||
QSSB_SYS(fcntl),
|
||||
QSSB_SYS(access),
|
||||
QSSB_SYS(open),
|
||||
QSSB_SYS(openat),
|
||||
QSSB_SYS(unlink),
|
||||
-1
|
||||
};
|
||||
|
||||
struct qssb_path_policy
|
||||
{
|
||||
const char *path;
|
||||
@ -173,6 +198,8 @@ struct qssb_policy
|
||||
int preserve_cwd;
|
||||
int not_dumpable;
|
||||
int no_new_privs;
|
||||
int no_fs;
|
||||
int no_new_fds;
|
||||
int namespace_options;
|
||||
/* Bind mounts all paths in path_policies into the chroot and applies
|
||||
non-landlock policies */
|
||||
@ -197,6 +224,8 @@ struct qssb_policy *qssb_init_policy()
|
||||
result->drop_caps = 1;
|
||||
result->not_dumpable = 1;
|
||||
result->no_new_privs = 1;
|
||||
result->no_fs = 0;
|
||||
result->no_new_fds = 0;
|
||||
result->namespace_options = QSSB_UNSHARE_MOUNT | QSSB_UNSHARE_USER;
|
||||
result->chdir_path = NULL;
|
||||
result->mount_path_policies_to_chroot = 0;
|
||||
@ -737,16 +766,101 @@ static int check_policy_sanity(struct qssb_policy *policy)
|
||||
}
|
||||
}
|
||||
|
||||
if(policy->path_policies != NULL && policy->mount_path_policies_to_chroot != 1)
|
||||
if(policy->path_policies != NULL)
|
||||
{
|
||||
#if HAVE_LANDLOCK != 1
|
||||
QSSB_LOG_ERROR("Path policies cannot be enforced! System needs landlock support or set mount_path_policies_to_chroot = 1\n");
|
||||
|
||||
if(policy->mount_path_policies_to_chroot != 1)
|
||||
{
|
||||
#if HAVE_LANDLOCK != 1
|
||||
QSSB_LOG_ERROR("Path policies cannot be enforced! System needs landlock support or set mount_path_policies_to_chroot = 1\n");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
if(policy->no_fs == 1)
|
||||
{
|
||||
QSSB_LOG_ERROR("If path_policies are specified, no_fs cannot be set to 1");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void close_file_fds()
|
||||
{
|
||||
long max_files = sysconf(_SC_OPEN_MAX);
|
||||
for(long i = 3; i <= max_files; i++)
|
||||
{
|
||||
close((int)i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Takes away file system access from the process
|
||||
*
|
||||
* We use this when "no_fs" is given in the policy.
|
||||
*
|
||||
* This is useful for restricted subprocesses that do some computational work
|
||||
* and do not require filesystem access
|
||||
*
|
||||
* @returns: 0 on success, < 0 on error
|
||||
*/
|
||||
static int enable_no_fs(struct qssb_policy *policy)
|
||||
{
|
||||
close_file_fds();
|
||||
|
||||
if(chdir("/proc/self/fdinfo") != 0)
|
||||
{
|
||||
QSSB_LOG_ERROR("Failed to change to safe directory: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(chroot(".") != 0)
|
||||
{
|
||||
QSSB_LOG_ERROR("Failed to chroot into safe directory: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(chdir("/") != 0)
|
||||
{
|
||||
QSSB_LOG_ERROR("Failed to chdir into safe directory inside chroot: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(policy->whitelisted_syscalls == NULL)
|
||||
{
|
||||
if(policy->blacklisted_syscalls == NULL)
|
||||
{
|
||||
policy->blacklisted_syscalls = fs_access_syscalls;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t nbl = 0;
|
||||
size_t nfs = sizeof(fs_access_syscalls)/sizeof(fs_access_syscalls[0]);
|
||||
int *tmp = policy->blacklisted_syscalls;
|
||||
while(*tmp != -1)
|
||||
{
|
||||
++nbl;
|
||||
++tmp;
|
||||
}
|
||||
size_t n = (nbl + nfs) + 1;
|
||||
tmp = (int *) calloc(n, sizeof(int));
|
||||
int *tmp_begin = tmp;
|
||||
if(tmp == NULL)
|
||||
{
|
||||
QSSB_LOG_ERROR("Failed to expand blacklisted syscall array\n");
|
||||
return -1;
|
||||
}
|
||||
memcpy(tmp, policy->blacklisted_syscalls, nbl*sizeof(int));
|
||||
tmp+=nbl;
|
||||
memcpy(tmp, fs_access_syscalls, nfs*sizeof(int));
|
||||
tmp+=(nfs+1);
|
||||
*tmp = -1;
|
||||
|
||||
policy->blacklisted_syscalls = tmp_begin;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enables the specified qssb_policy.
|
||||
*
|
||||
* This function is not atomic (and can't be). This means some
|
||||
@ -827,7 +941,6 @@ int qssb_enable_policy(struct qssb_policy *policy)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(policy->chdir_path == NULL)
|
||||
{
|
||||
policy->chdir_path = "/";
|
||||
@ -839,6 +952,25 @@ int qssb_enable_policy(struct qssb_policy *policy)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(policy->no_fs)
|
||||
{
|
||||
if(enable_no_fs(policy) != 0)
|
||||
{
|
||||
QSSB_LOG_ERROR("Failed to take away filesystem access of process\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(policy->no_new_fds)
|
||||
{
|
||||
const struct rlimit nofile = {0, 0};
|
||||
if (setrlimit(RLIMIT_NOFILE, &nofile) == -1)
|
||||
{
|
||||
QSSB_LOG_ERROR("setrlimit: Failed to set rlimit: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(policy->drop_caps)
|
||||
{
|
||||
if(drop_caps() < 0)
|
||||
|
68
test.c
68
test.c
@ -1,5 +1,10 @@
|
||||
#include "qssb.h"
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
int test_default_main(int argc, char *argv[])
|
||||
{
|
||||
struct qssb_policy *policy = qssb_init_policy();
|
||||
@ -69,6 +74,65 @@ int test_landlock_deny_write(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
int test_nofs(int argc, char *argv[])
|
||||
{
|
||||
struct qssb_policy *policy = qssb_init_policy();
|
||||
policy->no_fs = 1;
|
||||
|
||||
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);
|
||||
if(s == -1)
|
||||
{
|
||||
fprintf(stderr, "Failed to open socket but this was not requested by policy\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Expect seccomp to take care of this */
|
||||
if(open("/test", O_CREAT | O_WRONLY) >= 0)
|
||||
{
|
||||
fprintf(stderr, "Failed: Do not expect write access\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int test_no_new_fds(int argc, char *argv[])
|
||||
{
|
||||
struct qssb_policy *policy = qssb_init_policy();
|
||||
policy->no_new_fds = 1;
|
||||
|
||||
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)
|
||||
{
|
||||
fprintf(stderr, "Failed: Could open new file descriptor\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int s = socket(AF_INET,SOCK_STREAM,0);
|
||||
if(s >= 0)
|
||||
{
|
||||
fprintf(stderr, "Failed: socket got opened but policy denied\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
struct dispatcher
|
||||
{
|
||||
char *name;
|
||||
@ -81,7 +145,9 @@ struct dispatcher dispatchers[] = {
|
||||
{ "seccomp-blacklisted", &test_seccomp_blacklisted, false },
|
||||
{ "seccomp-blacklisted-permitted", &test_seccomp_blacklisted_call_permitted, true },
|
||||
{ "landlock", &test_landlock, true },
|
||||
{ "landlock-deny-write", &test_landlock_deny_write, true }
|
||||
{ "landlock-deny-write", &test_landlock_deny_write, true },
|
||||
{ "no_fs", &test_nofs, false},
|
||||
{ "no_new_fds", &test_no_new_fds, true}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
Memuat…
Reference in New Issue
Block a user