Skip to content

Commit

Permalink
landlock: Support socket access-control
Browse files Browse the repository at this point in the history
* Add new landlock rule type that corresponds to the restriction of
  socket protocols. This is represented as an landlock_socket_attr
  structure. Protocol allowed by landlock must be described by
  a family-type pair (see socket(2)).

* Support socket rule storage in landlock ruleset.

* Add flag LANDLOCK_ACCESS_SOCKET_CREATE that will provide the
  ability to control socket creation.

* Add socket.c file that will contain socket rules management and hooks.
  Implement helper pack_socket_key() to convert 32-bit family and type
  values into uintptr_t. This is possible due to the fact that these
  values are limited to AF_MAX (=46), SOCK_MAX (=11) constants. Assumption
  is checked in build-time by the helper.

* Support socket rules in landlock syscalls. Change ABI version to 6.

Closes: landlock-lsm#6
Signed-off-by: Mikhail Ivanov <[email protected]>
  • Loading branch information
sm1ling-knight authored and intel-lab-lkp committed May 24, 2024
1 parent 6d69b6c commit ec135c7
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 11 deletions.
53 changes: 52 additions & 1 deletion include/uapi/linux/landlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ struct landlock_ruleset_attr {
* rule explicitly allow them.
*/
__u64 handled_access_net;

/**
* @handled_access_socket: Bitmask of actions (cf. `Socket flags`_)
* that is handled by this ruleset and should then be forbidden if no
* rule explicitly allow them.
*/
__u64 handled_access_socket;
};

/*
Expand Down Expand Up @@ -65,6 +72,11 @@ enum landlock_rule_type {
* landlock_net_port_attr .
*/
LANDLOCK_RULE_NET_PORT,
/**
* @LANDLOCK_RULE_SOCKET: Type of a &struct
* landlock_socket_attr .
*/
LANDLOCK_RULE_SOCKET,
};

/**
Expand Down Expand Up @@ -115,6 +127,28 @@ struct landlock_net_port_attr {
__u64 port;
};

/**
* struct landlock_socket_attr - Socket definition
*
* Argument of sys_landlock_add_rule().
*/
struct landlock_socket_attr {
/**
* @allowed_access: Bitmask of allowed access for a socket
* (cf. `Socket flags`_).
*/
__u64 allowed_access;
/**
* @family: Protocol family used for communication
* (same as domain in socket(2)).
*/
int family;
/**
* @type: Socket type (see socket(2)).
*/
int type;
};

/**
* DOC: fs_access
*
Expand Down Expand Up @@ -251,7 +285,7 @@ struct landlock_net_port_attr {
* DOC: net_access
*
* Network flags
* ~~~~~~~~~~~~~~~~
* ~~~~~~~~~~~~~
*
* These flags enable to restrict a sandboxed process to a set of network
* actions. This is supported since the Landlock ABI version 4.
Expand All @@ -266,4 +300,21 @@ struct landlock_net_port_attr {
#define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0)
#define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1)
/* clang-format on */

/**
* DOC: socket_access
*
* Socket flags
* ~~~~~~~~~~~~
*
* These flags enable to restrict a sanboxed process to a set of socket
* protocols. This is supported since the Landlock ABI version 6.
*
* The following access rights apply only to sockets:
*
* - %LANDLOCK_ACCESS_SOCKET_CREATE: Create a socket.
*/
/* clang-format off */
#define LANDLOCK_ACCESS_SOCKET_CREATE (1ULL << 0)
/* clang-format on */
#endif /* _UAPI_LINUX_LANDLOCK_H */
2 changes: 1 addition & 1 deletion security/landlock/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o

landlock-y := setup.o syscalls.o object.o ruleset.o \
cred.o task.o fs.o
cred.o task.o fs.o socket.o

landlock-$(CONFIG_INET) += net.o
5 changes: 5 additions & 0 deletions security/landlock/limits.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
#define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET)
#define LANDLOCK_SHIFT_ACCESS_NET LANDLOCK_NUM_ACCESS_FS

#define LANDLOCK_LAST_ACCESS_SOCKET LANDLOCK_ACCESS_SOCKET_CREATE
#define LANDLOCK_MASK_ACCESS_SOCKET ((LANDLOCK_LAST_ACCESS_SOCKET << 1) - 1)
#define LANDLOCK_NUM_ACCESS_SOCKET __const_hweight64(LANDLOCK_MASK_ACCESS_SOCKET)
#define LANDLOCK_SHIFT_ACCESS_SOCKET LANDLOCK_NUM_ACCESS_SOCKET

/* clang-format on */

#endif /* _SECURITY_LANDLOCK_LIMITS_H */
37 changes: 34 additions & 3 deletions security/landlock/ruleset.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
#if IS_ENABLED(CONFIG_INET)
new_ruleset->root_net_port = RB_ROOT;
#endif /* IS_ENABLED(CONFIG_INET) */
new_ruleset->root_socket = RB_ROOT;

new_ruleset->num_layers = num_layers;
/*
Expand All @@ -52,12 +53,13 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)

struct landlock_ruleset *
landlock_create_ruleset(const access_mask_t fs_access_mask,
const access_mask_t net_access_mask)
const access_mask_t net_access_mask,
const access_mask_t socket_access_mask)
{
struct landlock_ruleset *new_ruleset;

/* Informs about useless ruleset. */
if (!fs_access_mask && !net_access_mask)
if (!fs_access_mask && !net_access_mask && !socket_access_mask)
return ERR_PTR(-ENOMSG);
new_ruleset = create_ruleset(1);
if (IS_ERR(new_ruleset))
Expand All @@ -66,6 +68,9 @@ landlock_create_ruleset(const access_mask_t fs_access_mask,
landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
if (net_access_mask)
landlock_add_net_access_mask(new_ruleset, net_access_mask, 0);
if (socket_access_mask)
landlock_add_socket_access_mask(new_ruleset, socket_access_mask,
0);
return new_ruleset;
}

Expand All @@ -89,6 +94,9 @@ static bool is_object_pointer(const enum landlock_key_type key_type)
return false;
#endif /* IS_ENABLED(CONFIG_INET) */

case LANDLOCK_KEY_SOCKET:
return false;

default:
WARN_ON_ONCE(1);
return false;
Expand Down Expand Up @@ -146,6 +154,9 @@ static struct rb_root *get_root(struct landlock_ruleset *const ruleset,
return &ruleset->root_net_port;
#endif /* IS_ENABLED(CONFIG_INET) */

case LANDLOCK_KEY_SOCKET:
return &ruleset->root_socket;

default:
WARN_ON_ONCE(1);
return ERR_PTR(-EINVAL);
Expand Down Expand Up @@ -175,7 +186,9 @@ static void build_check_ruleset(void)
BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
BUILD_BUG_ON(access_masks <
((LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS) |
(LANDLOCK_MASK_ACCESS_NET << LANDLOCK_SHIFT_ACCESS_NET)));
(LANDLOCK_MASK_ACCESS_NET << LANDLOCK_SHIFT_ACCESS_NET) |
(LANDLOCK_MASK_ACCESS_SOCKET
<< LANDLOCK_SHIFT_ACCESS_SOCKET)));
}

/**
Expand Down Expand Up @@ -399,6 +412,11 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
goto out_unlock;
#endif /* IS_ENABLED(CONFIG_INET) */

/* Merges the @src socket tree. */
err = merge_tree(dst, src, LANDLOCK_KEY_SOCKET);
if (err)
goto out_unlock;

out_unlock:
mutex_unlock(&src->lock);
mutex_unlock(&dst->lock);
Expand Down Expand Up @@ -462,6 +480,11 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
goto out_unlock;
#endif /* IS_ENABLED(CONFIG_INET) */

/* Copies the @parent socket tree. */
err = inherit_tree(parent, child, LANDLOCK_KEY_SOCKET);
if (err)
goto out_unlock;

if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
err = -EINVAL;
goto out_unlock;
Expand Down Expand Up @@ -498,6 +521,10 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
free_rule(freeme, LANDLOCK_KEY_NET_PORT);
#endif /* IS_ENABLED(CONFIG_INET) */

rbtree_postorder_for_each_entry_safe(freeme, next,
&ruleset->root_socket, node)
free_rule(freeme, LANDLOCK_KEY_SOCKET);

put_hierarchy(ruleset->hierarchy);
kfree(ruleset);
}
Expand Down Expand Up @@ -708,6 +735,10 @@ landlock_init_layer_masks(const struct landlock_ruleset *const domain,
break;
#endif /* IS_ENABLED(CONFIG_INET) */

case LANDLOCK_KEY_SOCKET:
get_access_mask = landlock_get_socket_access_mask;
num_access = LANDLOCK_NUM_ACCESS_SOCKET;
break;
default:
WARN_ON_ONCE(1);
return 0;
Expand Down
41 changes: 40 additions & 1 deletion security/landlock/ruleset.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ enum landlock_key_type {
* node keys.
*/
LANDLOCK_KEY_NET_PORT,

/**
* @LANDLOCK_KEY_SOCKET: Type of &landlock_ruleset.root_socket's
* node keys.
*/
LANDLOCK_KEY_SOCKET,
};

/**
Expand Down Expand Up @@ -177,6 +183,15 @@ struct landlock_ruleset {
struct rb_root root_net_port;
#endif /* IS_ENABLED(CONFIG_INET) */

/**
* @root_socket: Root of a red-black tree containing &struct
* landlock_rule nodes with socket type, described by (family, type)
* pair (see socket(2)). Once a ruleset is tied to a
* process (i.e. as a domain), this tree is immutable until @usage
* reaches zero.
*/
struct rb_root root_socket;

/**
* @hierarchy: Enables hierarchy identification even when a parent
* domain vanishes. This is needed for the ptrace protection.
Expand Down Expand Up @@ -233,7 +248,8 @@ struct landlock_ruleset {

struct landlock_ruleset *
landlock_create_ruleset(const access_mask_t access_mask_fs,
const access_mask_t access_mask_net);
const access_mask_t access_mask_net,
const access_mask_t access_mask_socket);

void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
Expand Down Expand Up @@ -282,6 +298,20 @@ landlock_add_net_access_mask(struct landlock_ruleset *const ruleset,
(net_mask << LANDLOCK_SHIFT_ACCESS_NET);
}

static inline void
landlock_add_socket_access_mask(struct landlock_ruleset *const ruleset,
const access_mask_t socket_access_mask,
const u16 layer_level)
{
access_mask_t socket_mask = socket_access_mask &
LANDLOCK_MASK_ACCESS_SOCKET;

/* Should already be checked in sys_landlock_create_ruleset(). */
WARN_ON_ONCE(socket_access_mask != socket_mask);
ruleset->access_masks[layer_level] |=
(socket_mask << LANDLOCK_SHIFT_ACCESS_SOCKET);
}

static inline access_mask_t
landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset,
const u16 layer_level)
Expand Down Expand Up @@ -309,6 +339,15 @@ landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset,
LANDLOCK_MASK_ACCESS_NET;
}

static inline access_mask_t
landlock_get_socket_access_mask(const struct landlock_ruleset *const ruleset,
const u16 layer_level)
{
return (ruleset->access_masks[layer_level] >>
LANDLOCK_SHIFT_ACCESS_SOCKET) &
LANDLOCK_MASK_ACCESS_SOCKET;
}

bool landlock_unmask_layers(const struct landlock_rule *const rule,
const access_mask_t access_request,
layer_mask_t (*const layer_masks)[],
Expand Down
60 changes: 60 additions & 0 deletions security/landlock/socket.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Landlock LSM - Socket management and hooks
*
* Copyright © 2024 Huawei Tech. Co., Ltd.
*/

#include <linux/net.h>
#include <linux/socket.h>
#include <linux/stddef.h>

#include "limits.h"
#include "ruleset.h"
#include "socket.h"

static uintptr_t pack_socket_key(const int family, const int type)
{
union {
struct {
unsigned short family, type;
} __packed data;
uintptr_t packed;
} socket_key;

/* Checks that all supported socket families and types can be stored in socket_key. */
BUILD_BUG_ON(AF_MAX > (typeof(socket_key.data.family))~0);
BUILD_BUG_ON(SOCK_MAX > (typeof(socket_key.data.type))~0);

/* Checks that socket_key can be stored in landlock_key. */
BUILD_BUG_ON(sizeof(socket_key.data) > sizeof(socket_key.packed));
BUILD_BUG_ON(sizeof(socket_key.packed) >
sizeof_field(union landlock_key, data));

socket_key.data.family = (unsigned short)family;
socket_key.data.type = (unsigned short)type;

return socket_key.packed;
}

int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
const int family, const int type,
access_mask_t access_rights)
{
int err;

const struct landlock_id id = {
.key.data = pack_socket_key(family, type),
.type = LANDLOCK_KEY_SOCKET,
};

/* Transforms relative access rights to absolute ones. */
access_rights |= LANDLOCK_MASK_ACCESS_SOCKET &
~landlock_get_socket_access_mask(ruleset, 0);

mutex_lock(&ruleset->lock);
err = landlock_insert_rule(ruleset, id, access_rights);
mutex_unlock(&ruleset->lock);

return err;
}
17 changes: 17 additions & 0 deletions security/landlock/socket.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Landlock LSM - Socket management and hooks
*
* Copyright © 2024 Huawei Tech. Co., Ltd.
*/

#ifndef _SECURITY_LANDLOCK_SOCKET_H
#define _SECURITY_LANDLOCK_SOCKET_H

#include "ruleset.h"

int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
const int family, const int type,
access_mask_t access_rights);

#endif /* _SECURITY_LANDLOCK_SOCKET_H */
Loading

0 comments on commit ec135c7

Please sign in to comment.