* Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
*/
#ifndef LANDLOCK_COMMON_H__
#define LANDLOCK_COMMON_H__
#include "tst_test.h"
#include "lapi/prctl.h"
#include "lapi/fcntl.h"
#include "lapi/landlock.h"
#define IPV4_LOCALHOST "127.0.0.1"
#define IPV6_LOCALHOST "::1"
struct socket_data {
struct sockaddr_in addr_ipv4;
struct sockaddr_in6 addr_ipv6;
size_t address_size;
int fd;
};
static inline int verify_landlock_is_enabled(void)
{
int abi;
abi = tst_syscall(__NR_landlock_create_ruleset,
NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
if (abi < 0) {
if (errno == EOPNOTSUPP) {
tst_brk(TCONF, "Landlock is currently disabled. "
"Please enable it either via CONFIG_LSM or "
"'lsm' kernel parameter.");
}
tst_brk(TBROK | TERRNO, "landlock_create_ruleset error");
}
tst_res(TINFO, "Landlock ABI v%d", abi);
return abi;
}
static inline void apply_landlock_fs_rule(
struct landlock_path_beneath_attr *path_beneath_attr,
const int ruleset_fd,
const int access,
const char *path)
{
path_beneath_attr->allowed_access = access;
path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH | O_CLOEXEC);
SAFE_LANDLOCK_ADD_RULE(
ruleset_fd,
LANDLOCK_RULE_PATH_BENEATH,
path_beneath_attr,
0);
SAFE_CLOSE(path_beneath_attr->parent_fd);
}
static inline void apply_landlock_net_rule(
struct landlock_net_port_attr *net_attr,
const int ruleset_fd,
const uint64_t port,
const uint64_t access)
{
net_attr->port = port;
net_attr->allowed_access = access;
SAFE_LANDLOCK_ADD_RULE(
ruleset_fd,
LANDLOCK_RULE_NET_PORT,
net_attr,
0);
}
static inline void enforce_ruleset(const int ruleset_fd)
{
SAFE_PRCTL(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
SAFE_LANDLOCK_RESTRICT_SELF(ruleset_fd, 0);
}
static inline void apply_landlock_fs_layer(
void *ruleset_attr, size_t attr_size,
struct landlock_path_beneath_attr *path_beneath_attr,
const char *path,
const int access)
{
int ruleset_fd;
ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET(ruleset_attr, attr_size, 0);
apply_landlock_fs_rule(path_beneath_attr, ruleset_fd, access, path);
enforce_ruleset(ruleset_fd);
SAFE_CLOSE(ruleset_fd);
}
static inline void apply_landlock_net_layer(
void *ruleset_attr, size_t attr_size,
struct landlock_net_port_attr *net_port_attr,
const in_port_t port,
const uint64_t access)
{
int ruleset_fd;
ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET(ruleset_attr, attr_size, 0);
apply_landlock_net_rule(net_port_attr, ruleset_fd, port, access);
enforce_ruleset(ruleset_fd);
SAFE_CLOSE(ruleset_fd);
}
static inline void apply_landlock_scoped_layer(
void *ruleset_attr, size_t attr_size)
{
int ruleset_fd;
ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET(ruleset_attr, attr_size, 0);
enforce_ruleset(ruleset_fd);
SAFE_CLOSE(ruleset_fd);
}
static inline in_port_t getsocket_port(struct socket_data *socket,
const int addr_family)
{
struct sockaddr_in addr_ipv4;
struct sockaddr_in6 addr_ipv6;
socklen_t len;
in_port_t port = 0;
switch (addr_family) {
case AF_INET:
len = sizeof(addr_ipv4);
memset(&addr_ipv4, 0, len);
SAFE_GETSOCKNAME(socket->fd, (struct sockaddr *)&addr_ipv4, &len);
port = ntohs(addr_ipv4.sin_port);
break;
case AF_INET6:
len = sizeof(addr_ipv6);
memset(&addr_ipv6, 0, len);
SAFE_GETSOCKNAME(socket->fd, (struct sockaddr *)&addr_ipv6, &len);
port = ntohs(addr_ipv6.sin6_port);
break;
default:
tst_brk(TBROK, "Unsupported protocol");
break;
};
return port;
}
static inline void create_socket(struct socket_data *socket,
const int addr_family, const in_port_t port)
{
memset(socket, 0, sizeof(struct socket_data));
switch (addr_family) {
case AF_INET:
if (!port) {
tst_init_sockaddr_inet_bin(&socket->addr_ipv4,
INADDR_ANY, 0);
} else {
tst_init_sockaddr_inet(&socket->addr_ipv4,
IPV4_LOCALHOST, port);
}
socket->address_size = sizeof(struct sockaddr_in);
break;
case AF_INET6:
if (!port) {
tst_init_sockaddr_inet6_bin(&socket->addr_ipv6,
&in6addr_any, 0);
} else {
tst_init_sockaddr_inet6(&socket->addr_ipv6,
IPV6_LOCALHOST, port);
}
socket->address_size = sizeof(struct sockaddr_in6);
break;
default:
tst_brk(TBROK, "Unsupported protocol");
return;
};
socket->fd = SAFE_SOCKET(addr_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
}
static inline void getsocket_addr(struct socket_data *socket,
const int addr_family, struct sockaddr **addr)
{
switch (addr_family) {
case AF_INET:
*addr = (struct sockaddr *)&socket->addr_ipv4;
break;
case AF_INET6:
*addr = (struct sockaddr *)&socket->addr_ipv6;
break;
default:
break;
};
}
#endif