mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-07-26 12:45:48 +00:00
Merge pull request #2347 from brauner/2018-05-24/seccomp_cleanups
seccomp: cleanup
This commit is contained in:
commit
b3365b9346
@ -2684,15 +2684,6 @@ out:
|
|||||||
return bret;
|
return bret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void strip_newline(char *p)
|
|
||||||
{
|
|
||||||
size_t len = strlen(p);
|
|
||||||
if (len < 1)
|
|
||||||
return;
|
|
||||||
if (p[len-1] == '\n')
|
|
||||||
p[len-1] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
void mod_all_rdeps(struct lxc_container *c, bool inc)
|
void mod_all_rdeps(struct lxc_container *c, bool inc)
|
||||||
{
|
{
|
||||||
struct lxc_container *p;
|
struct lxc_container *p;
|
||||||
@ -2715,8 +2706,10 @@ void mod_all_rdeps(struct lxc_container *c, bool inc)
|
|||||||
ERROR("badly formatted file %s", path);
|
ERROR("badly formatted file %s", path);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
strip_newline(lxcpath);
|
|
||||||
strip_newline(lxcname);
|
remove_trailing_newlines(lxcpath);
|
||||||
|
remove_trailing_newlines(lxcname);
|
||||||
|
|
||||||
if ((p = lxc_container_new(lxcname, lxcpath)) == NULL) {
|
if ((p = lxc_container_new(lxcname, lxcpath)) == NULL) {
|
||||||
ERROR("Unable to find dependent container %s:%s",
|
ERROR("Unable to find dependent container %s:%s",
|
||||||
lxcpath, lxcname);
|
lxcpath, lxcname);
|
||||||
|
@ -27,23 +27,24 @@
|
|||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
|
|
||||||
#ifdef HAVE_SECCOMP
|
#ifdef HAVE_SECCOMP
|
||||||
int lxc_seccomp_load(struct lxc_conf *conf);
|
extern int lxc_seccomp_load(struct lxc_conf *conf);
|
||||||
int lxc_read_seccomp_config(struct lxc_conf *conf);
|
extern int lxc_read_seccomp_config(struct lxc_conf *conf);
|
||||||
void lxc_seccomp_free(struct lxc_conf *conf);
|
extern void lxc_seccomp_free(struct lxc_conf *conf);
|
||||||
#else
|
#else
|
||||||
static inline int lxc_seccomp_load(struct lxc_conf *conf) {
|
static inline int lxc_seccomp_load(struct lxc_conf *conf)
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int lxc_read_seccomp_config(struct lxc_conf *conf) {
|
static inline int lxc_read_seccomp_config(struct lxc_conf *conf)
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void lxc_seccomp_free(struct lxc_conf *conf) {
|
static inline void lxc_seccomp_free(struct lxc_conf *conf)
|
||||||
if (conf->seccomp) {
|
{
|
||||||
free(conf->seccomp);
|
free(conf->seccomp);
|
||||||
conf->seccomp = NULL;
|
conf->seccomp = NULL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@
|
|||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <seccomp.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <seccomp.h>
|
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
|
||||||
@ -34,69 +34,49 @@
|
|||||||
#include "lxcseccomp.h"
|
#include "lxcseccomp.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#ifdef __MIPSEL__
|
||||||
|
#define MIPS_ARCH_O32 lxc_seccomp_arch_mipsel
|
||||||
|
#define MIPS_ARCH_N64 lxc_seccomp_arch_mipsel64
|
||||||
|
#else
|
||||||
|
#define MIPS_ARCH_O32 lxc_seccomp_arch_mips
|
||||||
|
#define MIPS_ARCH_N64 lxc_seccomp_arch_mips64
|
||||||
|
#endif
|
||||||
|
|
||||||
lxc_log_define(lxc_seccomp, lxc);
|
lxc_log_define(lxc_seccomp, lxc);
|
||||||
|
|
||||||
static int parse_config_v1(FILE *f, struct lxc_conf *conf)
|
static int parse_config_v1(FILE *f, struct lxc_conf *conf)
|
||||||
{
|
{
|
||||||
char line[1024];
|
int ret = 0;
|
||||||
int ret;
|
size_t line_bufsz = 0;
|
||||||
|
char *line = NULL;
|
||||||
|
|
||||||
while (fgets(line, 1024, f)) {
|
while (getline(&line, &line_bufsz, f) != -1) {
|
||||||
int nr;
|
int nr;
|
||||||
|
|
||||||
ret = sscanf(line, "%d", &nr);
|
ret = sscanf(line, "%d", &nr);
|
||||||
if (ret != 1)
|
if (ret != 1)
|
||||||
return -1;
|
return -1;
|
||||||
ret = seccomp_rule_add(
|
|
||||||
#if HAVE_SCMP_FILTER_CTX
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
conf->seccomp_ctx,
|
ret = seccomp_rule_add(conf->seccomp_ctx, SCMP_ACT_ALLOW, nr, 0);
|
||||||
|
#else
|
||||||
|
ret = seccomp_rule_add(SCMP_ACT_ALLOW, nr, 0);
|
||||||
#endif
|
#endif
|
||||||
SCMP_ACT_ALLOW, nr, 0);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ERROR("Failed loading allow rule for %d", nr);
|
ERROR("Failed loading allow rule for %d", nr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH
|
#if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH
|
||||||
static void remove_trailing_newlines(char *l)
|
|
||||||
{
|
|
||||||
char *p = l;
|
|
||||||
|
|
||||||
while (*p)
|
|
||||||
p++;
|
|
||||||
while (--p >= l && *p == '\n')
|
|
||||||
*p = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t get_v2_default_action(char *line)
|
|
||||||
{
|
|
||||||
uint32_t ret_action = -1;
|
|
||||||
|
|
||||||
while (*line == ' ')
|
|
||||||
line++;
|
|
||||||
/* After 'whitelist' or 'blacklist' comes default behavior. */
|
|
||||||
if (strncmp(line, "kill", 4) == 0)
|
|
||||||
ret_action = SCMP_ACT_KILL;
|
|
||||||
else if (strncmp(line, "errno", 5) == 0) {
|
|
||||||
int e;
|
|
||||||
if (sscanf(line + 5, "%d", &e) != 1) {
|
|
||||||
ERROR("Bad errno value in %s", line);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
ret_action = SCMP_ACT_ERRNO(e);
|
|
||||||
} else if (strncmp(line, "allow", 5) == 0)
|
|
||||||
ret_action = SCMP_ACT_ALLOW;
|
|
||||||
else if (strncmp(line, "trap", 4) == 0)
|
|
||||||
ret_action = SCMP_ACT_TRAP;
|
|
||||||
return ret_action;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *get_action_name(uint32_t action)
|
static const char *get_action_name(uint32_t action)
|
||||||
{
|
{
|
||||||
/* The upper 16 bits indicate the type of the seccomp action. */
|
/* The upper 16 bits indicate the type of the seccomp action. */
|
||||||
switch(action & 0xffff0000){
|
switch (action & 0xffff0000) {
|
||||||
case SCMP_ACT_KILL:
|
case SCMP_ACT_KILL:
|
||||||
return "kill";
|
return "kill";
|
||||||
case SCMP_ACT_ALLOW:
|
case SCMP_ACT_ALLOW:
|
||||||
@ -105,32 +85,68 @@ static const char *get_action_name(uint32_t action)
|
|||||||
return "trap";
|
return "trap";
|
||||||
case SCMP_ACT_ERRNO(0):
|
case SCMP_ACT_ERRNO(0):
|
||||||
return "errno";
|
return "errno";
|
||||||
default:
|
|
||||||
return "invalid action";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return "invalid action";
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t get_v2_default_action(char *line)
|
||||||
|
{
|
||||||
|
uint32_t ret_action = -1;
|
||||||
|
|
||||||
|
while (*line == ' ')
|
||||||
|
line++;
|
||||||
|
|
||||||
|
/* After 'whitelist' or 'blacklist' comes default behavior. */
|
||||||
|
if (strncmp(line, "kill", 4) == 0) {
|
||||||
|
ret_action = SCMP_ACT_KILL;
|
||||||
|
} else if (strncmp(line, "errno", 5) == 0) {
|
||||||
|
int e, ret;
|
||||||
|
|
||||||
|
ret = sscanf(line + 5, "%d", &e);
|
||||||
|
if (ret != 1) {
|
||||||
|
ERROR("Failed to parse errno value from %s", line);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret_action = SCMP_ACT_ERRNO(e);
|
||||||
|
} else if (strncmp(line, "allow", 5) == 0) {
|
||||||
|
ret_action = SCMP_ACT_ALLOW;
|
||||||
|
} else if (strncmp(line, "trap", 4) == 0) {
|
||||||
|
ret_action = SCMP_ACT_TRAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_action;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t get_v2_action(char *line, uint32_t def_action)
|
static uint32_t get_v2_action(char *line, uint32_t def_action)
|
||||||
{
|
{
|
||||||
char *p = strchr(line, ' ');
|
char *p;
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
|
|
||||||
|
p = strchr(line, ' ');
|
||||||
if (!p)
|
if (!p)
|
||||||
return def_action;
|
return def_action;
|
||||||
p++;
|
p++;
|
||||||
|
|
||||||
while (*p == ' ')
|
while (*p == ' ')
|
||||||
p++;
|
p++;
|
||||||
|
|
||||||
if (!*p || *p == '#')
|
if (!*p || *p == '#')
|
||||||
return def_action;
|
return def_action;
|
||||||
|
|
||||||
ret = get_v2_default_action(p);
|
ret = get_v2_default_action(p);
|
||||||
switch(ret) {
|
switch (ret) {
|
||||||
case -2: return -1;
|
case -2:
|
||||||
case -1: return def_action;
|
return -1;
|
||||||
default: return ret;
|
case -1:
|
||||||
|
return def_action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct v2_rule_args {
|
struct seccomp_v2_rule_args {
|
||||||
uint32_t index;
|
uint32_t index;
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
uint64_t mask;
|
uint64_t mask;
|
||||||
@ -140,7 +156,7 @@ struct v2_rule_args {
|
|||||||
struct seccomp_v2_rule {
|
struct seccomp_v2_rule {
|
||||||
uint32_t action;
|
uint32_t action;
|
||||||
uint32_t args_num;
|
uint32_t args_num;
|
||||||
struct v2_rule_args args_value[6];
|
struct seccomp_v2_rule_args args_value[6];
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum scmp_compare parse_v2_rule_op(char *s)
|
static enum scmp_compare parse_v2_rule_op(char *s)
|
||||||
@ -163,7 +179,8 @@ static enum scmp_compare parse_v2_rule_op(char *s)
|
|||||||
return _SCMP_CMP_MAX;
|
return _SCMP_CMP_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function is used to parse the args string into the structure.
|
/*
|
||||||
|
* This function is used to parse the args string into the structure.
|
||||||
* args string format:[index,value,op,valueTwo] or [index,value,op]
|
* args string format:[index,value,op,valueTwo] or [index,value,op]
|
||||||
* index: the index for syscall arguments (type uint)
|
* index: the index for syscall arguments (type uint)
|
||||||
* value: the value for syscall arguments (type uint64)
|
* value: the value for syscall arguments (type uint64)
|
||||||
@ -174,21 +191,21 @@ static enum scmp_compare parse_v2_rule_op(char *s)
|
|||||||
* valueTwo: the value for syscall arguments only used for mask eq (type uint64, optional)
|
* valueTwo: the value for syscall arguments only used for mask eq (type uint64, optional)
|
||||||
* Returns 0 on success, < 0 otherwise.
|
* Returns 0 on success, < 0 otherwise.
|
||||||
*/
|
*/
|
||||||
static int get_seccomp_arg_value(char *key, struct v2_rule_args *rule_args)
|
static int get_seccomp_arg_value(char *key, struct seccomp_v2_rule_args *rule_args)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
uint64_t value = 0;
|
|
||||||
uint64_t mask = 0;
|
|
||||||
enum scmp_compare op = 0;
|
|
||||||
uint32_t index = 0;
|
uint32_t index = 0;
|
||||||
char s[31] = {0}, v[24] = {0}, m[24] = {0};
|
uint64_t mask = 0, value = 0;
|
||||||
|
enum scmp_compare op = 0;
|
||||||
char *tmp = NULL;
|
char *tmp = NULL;
|
||||||
|
char s[31] = {0}, v[24] = {0}, m[24] = {0};
|
||||||
|
|
||||||
tmp = strchr(key, '[');
|
tmp = strchr(key, '[');
|
||||||
if (!tmp) {
|
if (!tmp) {
|
||||||
ERROR("Failed to interpret args");
|
ERROR("Failed to interpret args");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sscanf(tmp, "[%i,%23[^,],%30[^0-9^,],%23[^,]", &index, v, s, m);
|
ret = sscanf(tmp, "[%i,%23[^,],%30[^0-9^,],%23[^,]", &index, v, s, m);
|
||||||
if ((ret != 3 && ret != 4) || index >= 6) {
|
if ((ret != 3 && ret != 4) || index >= 6) {
|
||||||
ERROR("Failed to interpret args value");
|
ERROR("Failed to interpret args value");
|
||||||
@ -201,7 +218,7 @@ static int get_seccomp_arg_value(char *key, struct v2_rule_args *rule_args)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = lxc_safe_uint64(v, &mask);
|
ret = lxc_safe_uint64(m, &mask);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ERROR("Invalid argument mask");
|
ERROR("Invalid argument mask");
|
||||||
return -1;
|
return -1;
|
||||||
@ -226,13 +243,11 @@ static int get_seccomp_arg_value(char *key, struct v2_rule_args *rule_args)
|
|||||||
* @rules : output struct.
|
* @rules : output struct.
|
||||||
* Returns 0 on success, < 0 otherwise.
|
* Returns 0 on success, < 0 otherwise.
|
||||||
*/
|
*/
|
||||||
static int parse_v2_rules(char *line, uint32_t def_action, struct seccomp_v2_rule *rules)
|
static int parse_v2_rules(char *line, uint32_t def_action,
|
||||||
|
struct seccomp_v2_rule *rules)
|
||||||
{
|
{
|
||||||
int ret = 0 ;
|
int i = 0, ret = -1;
|
||||||
int i = 0;
|
char *key = NULL, *saveptr = NULL, *tmp = NULL;
|
||||||
char *tmp = NULL;
|
|
||||||
char *key = NULL;
|
|
||||||
char *saveptr = NULL;
|
|
||||||
|
|
||||||
tmp = strdup(line);
|
tmp = strdup(line);
|
||||||
if (!tmp)
|
if (!tmp)
|
||||||
@ -240,33 +255,29 @@ static int parse_v2_rules(char *line, uint32_t def_action, struct seccomp_v2_rul
|
|||||||
|
|
||||||
/* read optional action which follows the syscall */
|
/* read optional action which follows the syscall */
|
||||||
rules->action = get_v2_action(tmp, def_action);
|
rules->action = get_v2_action(tmp, def_action);
|
||||||
if (rules->action == -1) {
|
|
||||||
ERROR("Failed to interpret action");
|
|
||||||
ret = -1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
rules->args_num = 0;
|
|
||||||
if (!strchr(tmp, '[')) {
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
rules->args_num = 0;
|
||||||
|
if (!strchr(tmp, '['))
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
for ((key = strtok_r(tmp, "]", &saveptr)), i = 0; key && i < 6; (key = strtok_r(NULL, "]", &saveptr)), i++) {
|
|
||||||
ret = get_seccomp_arg_value(key, &rules->args_value[i]);
|
|
||||||
if (ret < 0) {
|
|
||||||
ret = -1;
|
ret = -1;
|
||||||
|
for ((key = strtok_r(tmp, "]", &saveptr)), i = 0; key && i < 6;
|
||||||
|
(key = strtok_r(NULL, "]", &saveptr)), i++) {
|
||||||
|
ret = get_seccomp_arg_value(key, &rules->args_value[i]);
|
||||||
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
rules->args_num++;
|
rules->args_num++;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
free(tmp);
|
free(tmp);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH
|
#if HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH
|
||||||
@ -291,14 +302,6 @@ enum lxc_hostarch_t {
|
|||||||
lxc_seccomp_arch_unknown = 999,
|
lxc_seccomp_arch_unknown = 999,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __MIPSEL__
|
|
||||||
# define MIPS_ARCH_O32 lxc_seccomp_arch_mipsel
|
|
||||||
# define MIPS_ARCH_N64 lxc_seccomp_arch_mipsel64
|
|
||||||
#else
|
|
||||||
# define MIPS_ARCH_O32 lxc_seccomp_arch_mips
|
|
||||||
# define MIPS_ARCH_N64 lxc_seccomp_arch_mips64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int get_hostarch(void)
|
int get_hostarch(void)
|
||||||
{
|
{
|
||||||
struct utsname uts;
|
struct utsname uts;
|
||||||
@ -306,6 +309,7 @@ int get_hostarch(void)
|
|||||||
SYSERROR("Failed to read host arch");
|
SYSERROR("Failed to read host arch");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(uts.machine, "i686") == 0)
|
if (strcmp(uts.machine, "i686") == 0)
|
||||||
return lxc_seccomp_arch_i386;
|
return lxc_seccomp_arch_i386;
|
||||||
/* no x32 kernels */
|
/* no x32 kernels */
|
||||||
@ -327,59 +331,96 @@ int get_hostarch(void)
|
|||||||
return MIPS_ARCH_O32;
|
return MIPS_ARCH_O32;
|
||||||
else if (strncmp(uts.machine, "s390x", 5) == 0)
|
else if (strncmp(uts.machine, "s390x", 5) == 0)
|
||||||
return lxc_seccomp_arch_s390x;
|
return lxc_seccomp_arch_s390x;
|
||||||
|
|
||||||
return lxc_seccomp_arch_unknown;
|
return lxc_seccomp_arch_unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
scmp_filter_ctx get_new_ctx(enum lxc_hostarch_t n_arch, uint32_t default_policy_action, bool *needs_merge)
|
scmp_filter_ctx get_new_ctx(enum lxc_hostarch_t n_arch,
|
||||||
|
uint32_t default_policy_action, bool *needs_merge)
|
||||||
{
|
{
|
||||||
scmp_filter_ctx ctx;
|
|
||||||
int ret;
|
int ret;
|
||||||
uint32_t arch;
|
uint32_t arch;
|
||||||
|
scmp_filter_ctx ctx;
|
||||||
|
|
||||||
switch(n_arch) {
|
switch (n_arch) {
|
||||||
case lxc_seccomp_arch_i386: arch = SCMP_ARCH_X86; break;
|
case lxc_seccomp_arch_i386:
|
||||||
case lxc_seccomp_arch_x32: arch = SCMP_ARCH_X32; break;
|
arch = SCMP_ARCH_X86;
|
||||||
case lxc_seccomp_arch_amd64: arch = SCMP_ARCH_X86_64; break;
|
break;
|
||||||
case lxc_seccomp_arch_arm: arch = SCMP_ARCH_ARM; break;
|
case lxc_seccomp_arch_x32:
|
||||||
|
arch = SCMP_ARCH_X32;
|
||||||
|
break;
|
||||||
|
case lxc_seccomp_arch_amd64:
|
||||||
|
arch = SCMP_ARCH_X86_64;
|
||||||
|
break;
|
||||||
|
case lxc_seccomp_arch_arm:
|
||||||
|
arch = SCMP_ARCH_ARM;
|
||||||
|
break;
|
||||||
#ifdef SCMP_ARCH_AARCH64
|
#ifdef SCMP_ARCH_AARCH64
|
||||||
case lxc_seccomp_arch_arm64: arch = SCMP_ARCH_AARCH64; break;
|
case lxc_seccomp_arch_arm64:
|
||||||
|
arch = SCMP_ARCH_AARCH64;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef SCMP_ARCH_PPC64LE
|
#ifdef SCMP_ARCH_PPC64LE
|
||||||
case lxc_seccomp_arch_ppc64le: arch = SCMP_ARCH_PPC64LE; break;
|
case lxc_seccomp_arch_ppc64le:
|
||||||
|
arch = SCMP_ARCH_PPC64LE;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef SCMP_ARCH_PPC64
|
#ifdef SCMP_ARCH_PPC64
|
||||||
case lxc_seccomp_arch_ppc64: arch = SCMP_ARCH_PPC64; break;
|
case lxc_seccomp_arch_ppc64:
|
||||||
|
arch = SCMP_ARCH_PPC64;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef SCMP_ARCH_PPC
|
#ifdef SCMP_ARCH_PPC
|
||||||
case lxc_seccomp_arch_ppc: arch = SCMP_ARCH_PPC; break;
|
case lxc_seccomp_arch_ppc:
|
||||||
|
arch = SCMP_ARCH_PPC;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef SCMP_ARCH_MIPS
|
#ifdef SCMP_ARCH_MIPS
|
||||||
case lxc_seccomp_arch_mips: arch = SCMP_ARCH_MIPS; break;
|
case lxc_seccomp_arch_mips:
|
||||||
case lxc_seccomp_arch_mips64: arch = SCMP_ARCH_MIPS64; break;
|
arch = SCMP_ARCH_MIPS;
|
||||||
case lxc_seccomp_arch_mips64n32: arch = SCMP_ARCH_MIPS64N32; break;
|
break;
|
||||||
case lxc_seccomp_arch_mipsel: arch = SCMP_ARCH_MIPSEL; break;
|
case lxc_seccomp_arch_mips64:
|
||||||
case lxc_seccomp_arch_mipsel64: arch = SCMP_ARCH_MIPSEL64; break;
|
arch = SCMP_ARCH_MIPS64;
|
||||||
case lxc_seccomp_arch_mipsel64n32: arch = SCMP_ARCH_MIPSEL64N32; break;
|
break;
|
||||||
|
case lxc_seccomp_arch_mips64n32:
|
||||||
|
arch = SCMP_ARCH_MIPS64N32;
|
||||||
|
break;
|
||||||
|
case lxc_seccomp_arch_mipsel:
|
||||||
|
arch = SCMP_ARCH_MIPSEL;
|
||||||
|
break;
|
||||||
|
case lxc_seccomp_arch_mipsel64:
|
||||||
|
arch = SCMP_ARCH_MIPSEL64;
|
||||||
|
break;
|
||||||
|
case lxc_seccomp_arch_mipsel64n32:
|
||||||
|
arch = SCMP_ARCH_MIPSEL64N32;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef SCMP_ARCH_S390X
|
#ifdef SCMP_ARCH_S390X
|
||||||
case lxc_seccomp_arch_s390x: arch = SCMP_ARCH_S390X; break;
|
case lxc_seccomp_arch_s390x:
|
||||||
|
arch = SCMP_ARCH_S390X;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default: return NULL;
|
default:
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ctx = seccomp_init(default_policy_action)) == NULL) {
|
ctx = seccomp_init(default_policy_action);
|
||||||
|
if (!ctx) {
|
||||||
ERROR("Error initializing seccomp context");
|
ERROR("Error initializing seccomp context");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0)) {
|
|
||||||
ERROR("Failed to turn off no-new-privs");
|
ret = seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
ERROR("%s - Failed to turn off no-new-privs", strerror(-ret));
|
||||||
seccomp_release(ctx);
|
seccomp_release(ctx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SCMP_FLTATR_ATL_TSKIP
|
#ifdef SCMP_FLTATR_ATL_TSKIP
|
||||||
if (seccomp_attr_set(ctx, SCMP_FLTATR_ATL_TSKIP, 1)) {
|
ret = seccomp_attr_set(ctx, SCMP_FLTATR_ATL_TSKIP, 1);
|
||||||
WARN("Failed to turn on seccomp nop-skip, continuing");
|
if (ret < 0)
|
||||||
}
|
WARN("%s - Failed to turn on seccomp nop-skip, continuing", strerror(-ret));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = seccomp_arch_exist(ctx, arch);
|
ret = seccomp_arch_exist(ctx, arch);
|
||||||
@ -421,16 +462,12 @@ scmp_filter_ctx get_new_ctx(enum lxc_hostarch_t n_arch, uint32_t default_policy_
|
|||||||
bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx,
|
bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx,
|
||||||
struct seccomp_v2_rule *rule)
|
struct seccomp_v2_rule *rule)
|
||||||
{
|
{
|
||||||
int nr, ret, i;
|
int i, nr, ret;
|
||||||
struct scmp_arg_cmp arg_cmp[6];
|
struct scmp_arg_cmp arg_cmp[6];
|
||||||
|
|
||||||
memset(arg_cmp, 0 ,sizeof(arg_cmp));
|
|
||||||
|
|
||||||
ret = seccomp_arch_exist(ctx, arch);
|
ret = seccomp_arch_exist(ctx, arch);
|
||||||
if (arch && ret != 0) {
|
if (arch && ret != 0) {
|
||||||
ERROR("BUG: Seccomp: rule and context arch do not match (arch "
|
ERROR("%s - Seccomp: rule and context arch do not match (arch %d)", strerror(-ret), arch);
|
||||||
"%d): %s",
|
|
||||||
arch, strerror(-ret));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,49 +477,59 @@ bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx,
|
|||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
||||||
if (strncmp(line, "reject_force_umount", 19) == 0) {
|
if (strncmp(line, "reject_force_umount", 19) == 0) {
|
||||||
INFO("Setting Seccomp rule to reject force umounts");
|
ret = seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EACCES),
|
||||||
ret = seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(umount2),
|
SCMP_SYS(umount2), 1,
|
||||||
1, SCMP_A1(SCMP_CMP_MASKED_EQ , MNT_FORCE , MNT_FORCE ));
|
SCMP_A1(SCMP_CMP_MASKED_EQ, MNT_FORCE, MNT_FORCE));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ERROR("Failed (%d) loading rule to reject force "
|
ERROR("%s - Failed loading rule to reject force umount", strerror(-ret));
|
||||||
"umount: %s",
|
|
||||||
ret, strerror(-ret));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INFO("Set seccomp rule to reject force umounts");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
nr = seccomp_syscall_resolve_name(line);
|
nr = seccomp_syscall_resolve_name(line);
|
||||||
if (nr == __NR_SCMP_ERROR) {
|
if (nr == __NR_SCMP_ERROR) {
|
||||||
WARN("Seccomp: failed to resolve syscall: %s", line);
|
WARN("Failed to resolve syscall \"%s\"", line);
|
||||||
WARN("This syscall will NOT be blacklisted");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (nr < 0) {
|
|
||||||
WARN("Seccomp: got negative for syscall: %d: %s", nr, line);
|
|
||||||
WARN("This syscall will NOT be blacklisted");
|
WARN("This syscall will NOT be blacklisted");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nr < 0) {
|
||||||
|
WARN("Got negative return value %d for syscall \"%s\"", nr, line);
|
||||||
|
WARN("This syscall will NOT be blacklisted");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&arg_cmp, 0, sizeof(arg_cmp));
|
||||||
for (i = 0; i < rule->args_num; i++) {
|
for (i = 0; i < rule->args_num; i++) {
|
||||||
INFO("arg_cmp[%d]:SCMP_CMP(%u, %llu, %llu, %llu)", i,
|
INFO("arg_cmp[%d]: SCMP_CMP(%u, %llu, %llu, %llu)", i,
|
||||||
rule->args_value[i].index,
|
rule->args_value[i].index,
|
||||||
(long long unsigned int)rule->args_value[i].op,
|
(long long unsigned int)rule->args_value[i].op,
|
||||||
(long long unsigned int)rule->args_value[i].mask,
|
(long long unsigned int)rule->args_value[i].mask,
|
||||||
(long long unsigned int)rule->args_value[i].value);
|
(long long unsigned int)rule->args_value[i].value);
|
||||||
|
|
||||||
if (SCMP_CMP_MASKED_EQ == rule->args_value[i].op)
|
if (SCMP_CMP_MASKED_EQ == rule->args_value[i].op)
|
||||||
arg_cmp[i] = SCMP_CMP(rule->args_value[i].index, rule->args_value[i].op, rule->args_value[i].mask, rule->args_value[i].value);
|
arg_cmp[i] = SCMP_CMP(rule->args_value[i].index,
|
||||||
|
rule->args_value[i].op,
|
||||||
|
rule->args_value[i].mask,
|
||||||
|
rule->args_value[i].value);
|
||||||
else
|
else
|
||||||
arg_cmp[i] = SCMP_CMP(rule->args_value[i].index, rule->args_value[i].op, rule->args_value[i].value);
|
arg_cmp[i] = SCMP_CMP(rule->args_value[i].index,
|
||||||
|
rule->args_value[i].op,
|
||||||
|
rule->args_value[i].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = seccomp_rule_add_exact_array(ctx, rule->action, nr, rule->args_num, arg_cmp);
|
ret = seccomp_rule_add_exact_array(ctx, rule->action, nr,
|
||||||
|
rule->args_num, arg_cmp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ERROR("Failed (%d) loading rule for %s (nr %d action %d(%s)): %s",
|
ERROR("%s - Failed loading rule for %s (nr %d action %d (%s))",
|
||||||
ret, line, nr, rule->action, get_action_name(rule->action), strerror(-ret));
|
strerror(-ret), line, nr, rule->action,
|
||||||
|
get_action_name(rule->action));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,12 +549,13 @@ bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx,
|
|||||||
*/
|
*/
|
||||||
static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
||||||
{
|
{
|
||||||
char *p;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
char *p;
|
||||||
|
enum lxc_hostarch_t cur_rule_arch, native_arch;
|
||||||
|
size_t line_bufsz = 0;
|
||||||
bool blacklist = false;
|
bool blacklist = false;
|
||||||
|
char *rule_line = NULL;
|
||||||
uint32_t default_policy_action = -1, default_rule_action = -1;
|
uint32_t default_policy_action = -1, default_rule_action = -1;
|
||||||
enum lxc_hostarch_t native_arch = get_hostarch(),
|
|
||||||
cur_rule_arch = native_arch;
|
|
||||||
struct seccomp_v2_rule rule;
|
struct seccomp_v2_rule rule;
|
||||||
struct scmp_ctx_info {
|
struct scmp_ctx_info {
|
||||||
uint32_t architectures[3];
|
uint32_t architectures[3];
|
||||||
@ -518,11 +566,12 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
if (strncmp(line, "blacklist", 9) == 0)
|
if (strncmp(line, "blacklist", 9) == 0)
|
||||||
blacklist = true;
|
blacklist = true;
|
||||||
else if (strncmp(line, "whitelist", 9) != 0) {
|
else if (strncmp(line, "whitelist", 9) != 0) {
|
||||||
ERROR("Bad seccomp policy style: %s", line);
|
ERROR("Bad seccomp policy style \"%s\"", line);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((p = strchr(line, ' '))) {
|
p = strchr(line, ' ');
|
||||||
|
if (p) {
|
||||||
default_policy_action = get_v2_default_action(p + 1);
|
default_policy_action = get_v2_default_action(p + 1);
|
||||||
if (default_policy_action == -2)
|
if (default_policy_action == -2)
|
||||||
return -1;
|
return -1;
|
||||||
@ -532,11 +581,13 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
if (blacklist) {
|
if (blacklist) {
|
||||||
if (default_policy_action == -1)
|
if (default_policy_action == -1)
|
||||||
default_policy_action = SCMP_ACT_ALLOW;
|
default_policy_action = SCMP_ACT_ALLOW;
|
||||||
|
|
||||||
if (default_rule_action == -1)
|
if (default_rule_action == -1)
|
||||||
default_rule_action = SCMP_ACT_KILL;
|
default_rule_action = SCMP_ACT_KILL;
|
||||||
} else {
|
} else {
|
||||||
if (default_policy_action == -1)
|
if (default_policy_action == -1)
|
||||||
default_policy_action = SCMP_ACT_KILL;
|
default_policy_action = SCMP_ACT_KILL;
|
||||||
|
|
||||||
if (default_rule_action == -1)
|
if (default_rule_action == -1)
|
||||||
default_rule_action = SCMP_ACT_ALLOW;
|
default_rule_action = SCMP_ACT_ALLOW;
|
||||||
}
|
}
|
||||||
@ -545,6 +596,8 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
ctx.architectures[0] = SCMP_ARCH_NATIVE;
|
ctx.architectures[0] = SCMP_ARCH_NATIVE;
|
||||||
ctx.architectures[1] = SCMP_ARCH_NATIVE;
|
ctx.architectures[1] = SCMP_ARCH_NATIVE;
|
||||||
ctx.architectures[2] = SCMP_ARCH_NATIVE;
|
ctx.architectures[2] = SCMP_ARCH_NATIVE;
|
||||||
|
native_arch = get_hostarch();
|
||||||
|
cur_rule_arch = native_arch;
|
||||||
if (native_arch == lxc_seccomp_arch_amd64) {
|
if (native_arch == lxc_seccomp_arch_amd64) {
|
||||||
cur_rule_arch = lxc_seccomp_arch_all;
|
cur_rule_arch = lxc_seccomp_arch_all;
|
||||||
|
|
||||||
@ -591,16 +644,16 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_all;
|
cur_rule_arch = lxc_seccomp_arch_all;
|
||||||
|
|
||||||
ctx.architectures[0] = SCMP_ARCH_ARM;
|
ctx.architectures[0] = SCMP_ARCH_ARM;
|
||||||
ctx.contexts[0] =
|
ctx.contexts[0] = get_new_ctx(lxc_seccomp_arch_arm,
|
||||||
get_new_ctx(lxc_seccomp_arch_arm, default_policy_action,
|
default_policy_action,
|
||||||
&ctx.needs_merge[0]);
|
&ctx.needs_merge[0]);
|
||||||
if (!ctx.contexts[0])
|
if (!ctx.contexts[0])
|
||||||
goto bad;
|
goto bad;
|
||||||
|
|
||||||
#ifdef SCMP_ARCH_AARCH64
|
#ifdef SCMP_ARCH_AARCH64
|
||||||
ctx.architectures[2] = SCMP_ARCH_AARCH64;
|
ctx.architectures[2] = SCMP_ARCH_AARCH64;
|
||||||
ctx.contexts[2] =
|
ctx.contexts[2] = get_new_ctx(lxc_seccomp_arch_arm64,
|
||||||
get_new_ctx(lxc_seccomp_arch_arm64, default_policy_action,
|
default_policy_action,
|
||||||
&ctx.needs_merge[2]);
|
&ctx.needs_merge[2]);
|
||||||
if (!ctx.contexts[2])
|
if (!ctx.contexts[2])
|
||||||
goto bad;
|
goto bad;
|
||||||
@ -662,25 +715,30 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
ERROR("Error re-initializing Seccomp");
|
ERROR("Error re-initializing Seccomp");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0)) {
|
|
||||||
ERROR("Failed to turn off no-new-privs");
|
ret = seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
ERROR("%s - Failed to turn off no-new-privs", strerror(-ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SCMP_FLTATR_ATL_TSKIP
|
#ifdef SCMP_FLTATR_ATL_TSKIP
|
||||||
if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1)) {
|
ret = seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1);
|
||||||
WARN("Failed to turn on seccomp nop-skip, continuing");
|
if (ret < 0)
|
||||||
}
|
WARN("%s - Failed to turn on seccomp nop-skip, continuing", strerror(-ret));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
while (fgets(line, 1024, f)) {
|
while (getline(&rule_line, &line_bufsz, f) != -1) {
|
||||||
|
|
||||||
if (line[0] == '#')
|
if (line[0] == '#')
|
||||||
continue;
|
continue;
|
||||||
if (strlen(line) == 0)
|
|
||||||
|
if (line[0] == '\0')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
remove_trailing_newlines(line);
|
remove_trailing_newlines(line);
|
||||||
INFO("processing: .%s", line);
|
|
||||||
|
INFO("Processing \"%s\"", line);
|
||||||
if (line[0] == '[') {
|
if (line[0] == '[') {
|
||||||
/* Read the architecture for next set of rules. */
|
/* Read the architecture for next set of rules. */
|
||||||
if (strcmp(line, "[x86]") == 0 ||
|
if (strcmp(line, "[x86]") == 0 ||
|
||||||
@ -690,6 +748,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_i386;
|
cur_rule_arch = lxc_seccomp_arch_i386;
|
||||||
} else if (strcmp(line, "[x32]") == 0 ||
|
} else if (strcmp(line, "[x32]") == 0 ||
|
||||||
strcmp(line, "[X32]") == 0) {
|
strcmp(line, "[X32]") == 0) {
|
||||||
@ -697,6 +756,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_x32;
|
cur_rule_arch = lxc_seccomp_arch_x32;
|
||||||
} else if (strcmp(line, "[X86_64]") == 0 ||
|
} else if (strcmp(line, "[X86_64]") == 0 ||
|
||||||
strcmp(line, "[x86_64]") == 0) {
|
strcmp(line, "[x86_64]") == 0) {
|
||||||
@ -704,6 +764,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_amd64;
|
cur_rule_arch = lxc_seccomp_arch_amd64;
|
||||||
} else if (strcmp(line, "[all]") == 0 ||
|
} else if (strcmp(line, "[all]") == 0 ||
|
||||||
strcmp(line, "[ALL]") == 0) {
|
strcmp(line, "[ALL]") == 0) {
|
||||||
@ -717,6 +778,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_arm;
|
cur_rule_arch = lxc_seccomp_arch_arm;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -727,6 +789,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_arm64;
|
cur_rule_arch = lxc_seccomp_arch_arm64;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -737,6 +800,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_ppc64le;
|
cur_rule_arch = lxc_seccomp_arch_ppc64le;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -747,6 +811,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_ppc64;
|
cur_rule_arch = lxc_seccomp_arch_ppc64;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -758,6 +823,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_ppc;
|
cur_rule_arch = lxc_seccomp_arch_ppc;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -768,6 +834,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_mips64;
|
cur_rule_arch = lxc_seccomp_arch_mips64;
|
||||||
} else if (strcmp(line, "[mips64n32]") == 0 ||
|
} else if (strcmp(line, "[mips64n32]") == 0 ||
|
||||||
strcmp(line, "[MIPS64N32]") == 0) {
|
strcmp(line, "[MIPS64N32]") == 0) {
|
||||||
@ -775,6 +842,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_mips64n32;
|
cur_rule_arch = lxc_seccomp_arch_mips64n32;
|
||||||
} else if (strcmp(line, "[mips]") == 0 ||
|
} else if (strcmp(line, "[mips]") == 0 ||
|
||||||
strcmp(line, "[MIPS]") == 0) {
|
strcmp(line, "[MIPS]") == 0) {
|
||||||
@ -783,6 +851,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_mips;
|
cur_rule_arch = lxc_seccomp_arch_mips;
|
||||||
} else if (strcmp(line, "[mipsel64]") == 0 ||
|
} else if (strcmp(line, "[mipsel64]") == 0 ||
|
||||||
strcmp(line, "[MIPSEL64]") == 0) {
|
strcmp(line, "[MIPSEL64]") == 0) {
|
||||||
@ -790,6 +859,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_mipsel64;
|
cur_rule_arch = lxc_seccomp_arch_mipsel64;
|
||||||
} else if (strcmp(line, "[mipsel64n32]") == 0 ||
|
} else if (strcmp(line, "[mipsel64n32]") == 0 ||
|
||||||
strcmp(line, "[MIPSEL64N32]") == 0) {
|
strcmp(line, "[MIPSEL64N32]") == 0) {
|
||||||
@ -797,6 +867,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_mipsel64n32;
|
cur_rule_arch = lxc_seccomp_arch_mipsel64n32;
|
||||||
} else if (strcmp(line, "[mipsel]") == 0 ||
|
} else if (strcmp(line, "[mipsel]") == 0 ||
|
||||||
strcmp(line, "[MIPSEL]") == 0) {
|
strcmp(line, "[MIPSEL]") == 0) {
|
||||||
@ -805,6 +876,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_mipsel;
|
cur_rule_arch = lxc_seccomp_arch_mipsel;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -815,11 +887,12 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
cur_rule_arch = lxc_seccomp_arch_unknown;
|
cur_rule_arch = lxc_seccomp_arch_unknown;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_rule_arch = lxc_seccomp_arch_s390x;
|
cur_rule_arch = lxc_seccomp_arch_s390x;
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
else
|
} else {
|
||||||
goto bad_arch;
|
goto bad_arch;
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -839,6 +912,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line,
|
if (!do_resolve_add_rule(SCMP_ARCH_NATIVE, line,
|
||||||
conf->seccomp_ctx, &rule))
|
conf->seccomp_ctx, &rule))
|
||||||
goto bad_rule;
|
goto bad_rule;
|
||||||
|
|
||||||
INFO("Added native rule for arch %d for %s action %d(%s)",
|
INFO("Added native rule for arch %d for %s action %d(%s)",
|
||||||
SCMP_ARCH_NATIVE, line, rule.action,
|
SCMP_ARCH_NATIVE, line, rule.action,
|
||||||
get_action_name(rule.action));
|
get_action_name(rule.action));
|
||||||
@ -847,6 +921,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
if (!do_resolve_add_rule(ctx.architectures[0], line,
|
if (!do_resolve_add_rule(ctx.architectures[0], line,
|
||||||
ctx.contexts[0], &rule))
|
ctx.contexts[0], &rule))
|
||||||
goto bad_rule;
|
goto bad_rule;
|
||||||
|
|
||||||
INFO("Added compat rule for arch %d for %s action %d(%s)",
|
INFO("Added compat rule for arch %d for %s action %d(%s)",
|
||||||
ctx.architectures[0], line, rule.action,
|
ctx.architectures[0], line, rule.action,
|
||||||
get_action_name(rule.action));
|
get_action_name(rule.action));
|
||||||
@ -856,6 +931,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
if (!do_resolve_add_rule(ctx.architectures[1], line,
|
if (!do_resolve_add_rule(ctx.architectures[1], line,
|
||||||
ctx.contexts[1], &rule))
|
ctx.contexts[1], &rule))
|
||||||
goto bad_rule;
|
goto bad_rule;
|
||||||
|
|
||||||
INFO("Added compat rule for arch %d for %s action %d(%s)",
|
INFO("Added compat rule for arch %d for %s action %d(%s)",
|
||||||
ctx.architectures[1], line, rule.action,
|
ctx.architectures[1], line, rule.action,
|
||||||
get_action_name(rule.action));
|
get_action_name(rule.action));
|
||||||
@ -865,6 +941,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
if (!do_resolve_add_rule(ctx.architectures[2], line,
|
if (!do_resolve_add_rule(ctx.architectures[2], line,
|
||||||
ctx.contexts[2], &rule))
|
ctx.contexts[2], &rule))
|
||||||
goto bad_rule;
|
goto bad_rule;
|
||||||
|
|
||||||
INFO("Added native rule for arch %d for %s action %d(%s)",
|
INFO("Added native rule for arch %d for %s action %d(%s)",
|
||||||
ctx.architectures[2], line, rule.action,
|
ctx.architectures[2], line, rule.action,
|
||||||
get_action_name(rule.action));
|
get_action_name(rule.action));
|
||||||
@ -880,6 +957,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
"context into main context");
|
"context into main context");
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Merged first compat seccomp context into main context");
|
TRACE("Merged first compat seccomp context into main context");
|
||||||
} else {
|
} else {
|
||||||
seccomp_release(ctx.contexts[0]);
|
seccomp_release(ctx.contexts[0]);
|
||||||
@ -895,6 +973,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
"context into main context");
|
"context into main context");
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Merged second compat seccomp context into main context");
|
TRACE("Merged second compat seccomp context into main context");
|
||||||
} else {
|
} else {
|
||||||
seccomp_release(ctx.contexts[1]);
|
seccomp_release(ctx.contexts[1]);
|
||||||
@ -910,6 +989,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
"context into main context");
|
"context into main context");
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Merged third compat seccomp context into main context");
|
TRACE("Merged third compat seccomp context into main context");
|
||||||
} else {
|
} else {
|
||||||
seccomp_release(ctx.contexts[2]);
|
seccomp_release(ctx.contexts[2]);
|
||||||
@ -917,19 +997,25 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(rule_line);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bad_arch:
|
bad_arch:
|
||||||
ERROR("Unsupported arch: %s.", line);
|
ERROR("Unsupported architecture \"%s\"", line);
|
||||||
|
|
||||||
bad_rule:
|
bad_rule:
|
||||||
bad:
|
bad:
|
||||||
if (ctx.contexts[0])
|
if (ctx.contexts[0])
|
||||||
seccomp_release(ctx.contexts[0]);
|
seccomp_release(ctx.contexts[0]);
|
||||||
|
|
||||||
if (ctx.contexts[1])
|
if (ctx.contexts[1])
|
||||||
seccomp_release(ctx.contexts[1]);
|
seccomp_release(ctx.contexts[1]);
|
||||||
|
|
||||||
if (ctx.contexts[2])
|
if (ctx.contexts[2])
|
||||||
seccomp_release(ctx.contexts[2]);
|
seccomp_release(ctx.contexts[2]);
|
||||||
|
|
||||||
|
free(rule_line);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#else /* HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH */
|
#else /* HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH */
|
||||||
@ -949,7 +1035,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
|
|||||||
*/
|
*/
|
||||||
static int parse_config(FILE *f, struct lxc_conf *conf)
|
static int parse_config(FILE *f, struct lxc_conf *conf)
|
||||||
{
|
{
|
||||||
char line[1024];
|
char line[MAXPATHLEN];
|
||||||
int ret, version;
|
int ret, version;
|
||||||
|
|
||||||
ret = fscanf(f, "%d\n", &version);
|
ret = fscanf(f, "%d\n", &version);
|
||||||
@ -957,10 +1043,12 @@ static int parse_config(FILE *f, struct lxc_conf *conf)
|
|||||||
ERROR("Invalid version");
|
ERROR("Invalid version");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (!fgets(line, 1024, f)) {
|
|
||||||
|
if (!fgets(line, MAXPATHLEN, f)) {
|
||||||
ERROR("Invalid config file");
|
ERROR("Invalid config file");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version == 1 && !strstr(line, "whitelist")) {
|
if (version == 1 && !strstr(line, "whitelist")) {
|
||||||
ERROR("Only whitelist policy is supported");
|
ERROR("Only whitelist policy is supported");
|
||||||
return -1;
|
return -1;
|
||||||
@ -973,6 +1061,7 @@ static int parse_config(FILE *f, struct lxc_conf *conf)
|
|||||||
|
|
||||||
if (version == 1)
|
if (version == 1)
|
||||||
return parse_config_v1(f, conf);
|
return parse_config_v1(f, conf);
|
||||||
|
|
||||||
return parse_config_v2(f, line, conf);
|
return parse_config_v2(f, line, conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -985,48 +1074,54 @@ static int parse_config(FILE *f, struct lxc_conf *conf)
|
|||||||
*/
|
*/
|
||||||
static bool use_seccomp(void)
|
static bool use_seccomp(void)
|
||||||
{
|
{
|
||||||
FILE *f = fopen("/proc/self/status", "r");
|
|
||||||
char line[1024];
|
|
||||||
bool already_enabled = false;
|
|
||||||
bool found = false;
|
|
||||||
int ret, v;
|
int ret, v;
|
||||||
|
FILE *f;
|
||||||
|
size_t line_bufsz = 0;
|
||||||
|
char *line = NULL;
|
||||||
|
bool already_enabled = false, found = false;
|
||||||
|
|
||||||
|
f = fopen("/proc/self/status", "r");
|
||||||
if (!f)
|
if (!f)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
while (fgets(line, 1024, f)) {
|
while (getline(&line, &line_bufsz, f) != -1) {
|
||||||
if (strncmp(line, "Seccomp:", 8) == 0) {
|
if (strncmp(line, "Seccomp:", 8) == 0) {
|
||||||
found = true;
|
found = true;
|
||||||
|
|
||||||
ret = sscanf(line + 8, "%d", &v);
|
ret = sscanf(line + 8, "%d", &v);
|
||||||
if (ret == 1 && v != 0)
|
if (ret == 1 && v != 0)
|
||||||
already_enabled = true;
|
already_enabled = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
free(line);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
if (!found) { /* no Seccomp line, no seccomp in kernel */
|
|
||||||
|
if (!found) {
|
||||||
INFO("Seccomp is not enabled in the kernel");
|
INFO("Seccomp is not enabled in the kernel");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (already_enabled) { /* already seccomp-confined */
|
|
||||||
|
if (already_enabled) {
|
||||||
INFO("Already seccomp-confined, not loading new policy");
|
INFO("Already seccomp-confined, not loading new policy");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lxc_read_seccomp_config(struct lxc_conf *conf)
|
int lxc_read_seccomp_config(struct lxc_conf *conf)
|
||||||
{
|
{
|
||||||
|
int check_seccomp_attr_set, ret;
|
||||||
FILE *f;
|
FILE *f;
|
||||||
int ret;
|
|
||||||
int check_seccomp_attr_set;
|
|
||||||
|
|
||||||
if (!conf->seccomp)
|
if (!conf->seccomp)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!use_seccomp())
|
if (!use_seccomp())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#if HAVE_SCMP_FILTER_CTX
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
/* XXX for debug, pass in SCMP_ACT_TRAP */
|
/* XXX for debug, pass in SCMP_ACT_TRAP */
|
||||||
conf->seccomp_ctx = seccomp_init(SCMP_ACT_KILL);
|
conf->seccomp_ctx = seccomp_init(SCMP_ACT_KILL);
|
||||||
@ -1047,13 +1142,14 @@ int lxc_read_seccomp_config(struct lxc_conf *conf)
|
|||||||
check_seccomp_attr_set = seccomp_attr_set(SCMP_FLTATR_CTL_NNP, 0);
|
check_seccomp_attr_set = seccomp_attr_set(SCMP_FLTATR_CTL_NNP, 0);
|
||||||
#endif
|
#endif
|
||||||
if (check_seccomp_attr_set) {
|
if (check_seccomp_attr_set) {
|
||||||
ERROR("Failed to turn off no-new-privs");
|
ERROR("%s - Failed to turn off no-new-privs", strerror(-check_seccomp_attr_set));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#ifdef SCMP_FLTATR_ATL_TSKIP
|
#ifdef SCMP_FLTATR_ATL_TSKIP
|
||||||
if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1)) {
|
check_seccomp_attr_set = seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_ATL_TSKIP, 1);
|
||||||
WARN("Failed to turn on seccomp nop-skip, continuing");
|
if (check_seccomp_attr_set < 0)
|
||||||
}
|
WARN("%s - Failed to turn on seccomp nop-skip, continuing",
|
||||||
|
strerror(-check_seccomp_attr_set));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
f = fopen(conf->seccomp, "r");
|
f = fopen(conf->seccomp, "r");
|
||||||
@ -1061,39 +1157,46 @@ int lxc_read_seccomp_config(struct lxc_conf *conf)
|
|||||||
SYSERROR("Failed to open seccomp policy file %s", conf->seccomp);
|
SYSERROR("Failed to open seccomp policy file %s", conf->seccomp);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = parse_config(f, conf);
|
ret = parse_config(f, conf);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lxc_seccomp_load(struct lxc_conf *conf)
|
int lxc_seccomp_load(struct lxc_conf *conf)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!conf->seccomp)
|
if (!conf->seccomp)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!use_seccomp())
|
if (!use_seccomp())
|
||||||
return 0;
|
return 0;
|
||||||
ret = seccomp_load(
|
|
||||||
#if HAVE_SCMP_FILTER_CTX
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
conf->seccomp_ctx
|
ret = seccomp_load(conf->seccomp_ctx);
|
||||||
|
#else
|
||||||
|
ret = seccomp_load();
|
||||||
#endif
|
#endif
|
||||||
);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ERROR("Error loading the seccomp policy: %s", strerror(-ret));
|
ERROR("%s- Error loading the seccomp policy", strerror(-ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* After load seccomp filter into the kernel successfully, export the current seccomp
|
/* After load seccomp filter into the kernel successfully, export the current seccomp
|
||||||
* filter to log file */
|
* filter to log file */
|
||||||
#if HAVE_SCMP_FILTER_CTX
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
if ((lxc_log_get_level() <= LXC_LOG_LEVEL_TRACE || conf->loglevel <= LXC_LOG_LEVEL_TRACE) &&
|
if ((lxc_log_get_level() <= LXC_LOG_LEVEL_TRACE ||
|
||||||
|
conf->loglevel <= LXC_LOG_LEVEL_TRACE) &&
|
||||||
lxc_log_fd >= 0) {
|
lxc_log_fd >= 0) {
|
||||||
ret = seccomp_export_pfc(conf->seccomp_ctx, lxc_log_fd);
|
ret = seccomp_export_pfc(conf->seccomp_ctx, lxc_log_fd);
|
||||||
/* Just give an warning when export error */
|
/* Just give an warning when export error */
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
WARN("Failed to export seccomp filter to log file: %s", strerror(-ret));
|
WARN("%s - Failed to export seccomp filter to log file", strerror(-ret));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1101,6 +1204,7 @@ void lxc_seccomp_free(struct lxc_conf *conf)
|
|||||||
{
|
{
|
||||||
free(conf->seccomp);
|
free(conf->seccomp);
|
||||||
conf->seccomp = NULL;
|
conf->seccomp = NULL;
|
||||||
|
|
||||||
#if HAVE_SCMP_FILTER_CTX
|
#if HAVE_SCMP_FILTER_CTX
|
||||||
if (conf->seccomp_ctx) {
|
if (conf->seccomp_ctx) {
|
||||||
seccomp_release(conf->seccomp_ctx);
|
seccomp_release(conf->seccomp_ctx);
|
||||||
|
@ -2533,3 +2533,14 @@ int lxc_set_death_signal(int signal)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void remove_trailing_newlines(char *l)
|
||||||
|
{
|
||||||
|
char *p = l;
|
||||||
|
|
||||||
|
while (*p)
|
||||||
|
p++;
|
||||||
|
|
||||||
|
while (--p >= l && *p == '\n')
|
||||||
|
*p = '\0';
|
||||||
|
}
|
||||||
|
@ -453,6 +453,7 @@ extern void lxc_free_array(void **array, lxc_free_fn element_free_fn);
|
|||||||
extern size_t lxc_array_len(void **array);
|
extern size_t lxc_array_len(void **array);
|
||||||
|
|
||||||
extern void **lxc_append_null_to_array(void **array, size_t count);
|
extern void **lxc_append_null_to_array(void **array, size_t count);
|
||||||
|
extern void remove_trailing_newlines(char *l);
|
||||||
|
|
||||||
/* initialize rand with urandom */
|
/* initialize rand with urandom */
|
||||||
extern int randseed(bool);
|
extern int randseed(bool);
|
||||||
|
Loading…
Reference in New Issue
Block a user