Begin landlock support

This commit is contained in:
Albert S. 2021-05-13 18:21:37 +02:00
parent 7e2d4139cb
commit bb02e40101

160
qssb.h
View File

@ -50,6 +50,9 @@
#endif #endif
#if HAVE_LANDLOCK == 1 #if HAVE_LANDLOCK == 1
#include <linux/landlock.h> #include <linux/landlock.h>
#if LANDLOCK_CREATE_RULESET_VERSION != (1U << 0)
#error "This landlock ABI version is not supported by qssb (yet)"
#endif
#endif #endif
//TODO: stolen from kernel samples/seccomp, GPLv2...? //TODO: stolen from kernel samples/seccomp, GPLv2...?
@ -96,8 +99,36 @@
#define QSSB_FS_ALLOW_MAKE_FIFO (1 << 13) #define QSSB_FS_ALLOW_MAKE_FIFO (1 << 13)
#define QSSB_FS_ALLOW_MAKE_BLOCK (1 << 14) #define QSSB_FS_ALLOW_MAKE_BLOCK (1 << 14)
#define QSSB_FS_ALLOW_MAKE_SYM (1 << 15) #define QSSB_FS_ALLOW_MAKE_SYM (1 << 15)
#define QSSB_FS_ALLOW_WRITE_FILE (1 << 16)
#define QSSB_FS_ALLOW_READ_DIR (1 << 17)
#define QSSB_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 #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
/* Most exploits have more need for those syscalls than the /* Most exploits have more need for those syscalls than the
* exploited programs. In cases they are needed, this list should be * exploited programs. In cases they are needed, this list should be
@ -146,7 +177,6 @@ struct qssb_policy
struct qssb_path_policy *path_policies; struct qssb_path_policy *path_policies;
}; };
/* Creates the default policy /* Creates the default policy
* Must be freed using qssb_free_policy * Must be freed using qssb_free_policy
* @returns: default policy */ * @returns: default policy */
@ -508,9 +538,117 @@ static int seccomp_enable_whitelist(int *syscalls)
} }
#if HAVE_LANDLOCK == 1 #if HAVE_LANDLOCK == 1
static int enable_landlock_policies(struct qssb_path_policy *policies) static unsigned int qssb_flags_to_landlock(unsigned int flags)
{ {
return 0; unsigned int result = 0;
if(flags & QSSB_FS_ALLOW_DEV)
{
result |= LANDLOCK_ACCESS_FS_MAKE_BLOCK;
result |= LANDLOCK_ACCESS_FS_MAKE_CHAR;
}
if(flags & QSSB_FS_ALLOW_MAKE_BLOCK)
{
result |= LANDLOCK_ACCESS_FS_MAKE_BLOCK;
}
if(flags & QSSB_FS_ALLOW_MAKE_CHAR)
{
result |= LANDLOCK_ACCESS_FS_MAKE_CHAR;
}
if(flags & QSSB_FS_ALLOW_MAKE_DIR)
{
result |= LANDLOCK_ACCESS_FS_MAKE_DIR;
}
if(flags & QSSB_FS_ALLOW_MAKE_FIFO)
{
result |= LANDLOCK_ACCESS_FS_MAKE_FIFO;
}
if(flags & QSSB_FS_ALLOW_MAKE_REG)
{
result |= LANDLOCK_ACCESS_FS_MAKE_REG;
}
if(flags & QSSB_FS_ALLOW_MAKE_SOCK)
{
result |= LANDLOCK_ACCESS_FS_MAKE_SOCK;
}
if(flags & QSSB_FS_ALLOW_MAKE_SYM)
{
result |= LANDLOCK_ACCESS_FS_MAKE_SYM;
}
if(flags & QSSB_FS_ALLOW_READ)
{
result |= LANDLOCK_ACCESS_FS_READ_FILE;
result |= LANDLOCK_ACCESS_FS_READ_DIR;
}
if(flags & QSSB_FS_ALLOW_REMOVE)
{
result |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
result |= LANDLOCK_ACCESS_FS_REMOVE_FILE;
}
if(flags & QSSB_FS_ALLOW_REMOVE_DIR)
{
result |= LANDLOCK_ACCESS_FS_REMOVE_DIR;
}
if(flags & QSSB_FS_ALLOW_REMOVE_FILE)
{
result |= LANDLOCK_ACCESS_FS_REMOVE_FILE;
}
if(flags & QSSB_FS_ALLOW_EXEC)
{
result |= LANDLOCK_ACCESS_FS_EXECUTE;
}
if(flags & QSSB_FS_ALLOW_WRITE)
{
result |= LANDLOCK_ACCESS_FS_MAKE_REG;
result |= LANDLOCK_ACCESS_FS_WRITE_FILE;
}
if(flags & QSSB_FS_ALLOW_WRITE_FILE)
{
result |= LANDLOCK_ACCESS_FS_WRITE_FILE;
}
if(flags & QSSB_FS_ALLOW_READ_DIR)
{
result |= LANDLOCK_ACCESS_FS_READ_DIR;
}
return result;
}
static int landlock_prepare_ruleset(struct qssb_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);
ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
if (ruleset_fd < 0)
{
QSSB_LOG_ERROR("Failed to create landlock ruleset");
return -1;
}
struct qssb_path_policy *policy = policies;
while(policy != NULL)
{
struct landlock_path_beneath_attr path_beneath;
path_beneath.parent_fd = open(policy->path, O_PATH | O_CLOEXEC);
if(path_beneath.parent_fd < 0)
{
QSSB_LOG_ERROR("Failed to open policy path %s while preparing landlock ruleset\n", policy->path);
close(ruleset_fd);
return path_beneath.parent_fd;
}
path_beneath.allowed_access = qssb_flags_to_landlock(policy->policy);
int ret = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0);
if(ret)
{
QSSB_LOG_ERROR("Failed to update ruleset while processsing policy path %s\n", policy->path);
close(ruleset_fd);
return ret;
}
policy = policy->next;
}
return ruleset_fd;
} }
#endif #endif
@ -620,11 +758,13 @@ int qssb_enable_policy(struct qssb_policy *policy)
} }
#if HAVE_LANDLOCK == 1 #if HAVE_LANDLOCK == 1
int landlock_ruleset_fd = -1;
if(policy->path_policies != NULL) if(policy->path_policies != NULL)
{ {
if(enable_landlock_policies(policy->path_policies) < 0) landlock_ruleset_fd = landlock_prepare_ruleset(policy->path_policies);
if(landlock_ruleset_fd < 0)
{ {
QSSB_LOG_ERROR("enable_landlock_policies: Failed to enable landlock policies\n"); QSSB_LOG_ERROR("landlock_prepare_ruleset: Failed to prepare landlock ruleset: %s\n", strerror(errno));
return -1; return -1;
} }
} }
@ -668,6 +808,16 @@ int qssb_enable_policy(struct qssb_policy *policy)
} }
} }
#if HAVE_LANDLOCK == 1
if (policy->path_policies != NULL && landlock_restrict_self(landlock_ruleset_fd, 0) != 0)
{
perror("Failed to enforce ruleset");
close(landlock_ruleset_fd);
return -1;
}
close(landlock_ruleset_fd);
#endif
if(policy->allowed_syscalls != NULL) if(policy->allowed_syscalls != NULL)
{ {
if(seccomp_enable_whitelist(policy->allowed_syscalls) <0) if(seccomp_enable_whitelist(policy->allowed_syscalls) <0)