summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/progs/lsm_cgroup.c
blob: d7598538aa2dad1565727ffcfe4ed66ec6f8fc63 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// SPDX-License-Identifier: GPL-2.0

#include "vmlinux.h"
#include "bpf_tracing_net.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

char _license[] SEC("license") = "GPL";

extern bool CONFIG_SECURITY_SELINUX __kconfig __weak;
extern bool CONFIG_SECURITY_SMACK __kconfig __weak;
extern bool CONFIG_SECURITY_APPARMOR __kconfig __weak;

#ifndef AF_PACKET
#define AF_PACKET 17
#endif

#ifndef AF_UNIX
#define AF_UNIX 1
#endif

#ifndef EPERM
#define EPERM 1
#endif

struct {
	__uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
	__type(key, __u64);
	__type(value, __u64);
} cgroup_storage SEC(".maps");

int called_socket_post_create;
int called_socket_post_create2;
int called_socket_bind;
int called_socket_bind2;
int called_socket_alloc;
int called_socket_clone;

static __always_inline int test_local_storage(void)
{
	__u64 *val;

	val = bpf_get_local_storage(&cgroup_storage, 0);
	if (!val)
		return 0;
	*val += 1;

	return 1;
}

static __always_inline int real_create(struct socket *sock, int family,
				       int protocol)
{
	struct sock *sk;
	int prio = 123;

	/* Reject non-tx-only AF_PACKET. */
	if (family == AF_PACKET && protocol != 0)
		return 0; /* EPERM */

	sk = sock->sk;
	if (!sk)
		return 1;

	/* The rest of the sockets get default policy. */
	if (bpf_setsockopt(sk, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)))
		return 0; /* EPERM */

	/* Make sure bpf_getsockopt is allowed and works. */
	prio = 0;
	if (bpf_getsockopt(sk, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)))
		return 0; /* EPERM */
	if (prio != 123)
		return 0; /* EPERM */

	/* Can access cgroup local storage. */
	if (!test_local_storage())
		return 0; /* EPERM */

	return 1;
}

/* __cgroup_bpf_run_lsm_socket */
SEC("lsm_cgroup/socket_post_create")
int BPF_PROG(socket_post_create, struct socket *sock, int family,
	     int type, int protocol, int kern)
{
	called_socket_post_create++;
	return real_create(sock, family, protocol);
}

/* __cgroup_bpf_run_lsm_socket */
SEC("lsm_cgroup/socket_post_create")
int BPF_PROG(socket_post_create2, struct socket *sock, int family,
	     int type, int protocol, int kern)
{
	called_socket_post_create2++;
	return real_create(sock, family, protocol);
}

static __always_inline int real_bind(struct socket *sock,
				     struct sockaddr *address,
				     int addrlen)
{
	struct sockaddr_ll sa = {};
	struct sock *sk = sock->sk;

	if (!sk)
		return 1;

	if (sk->__sk_common.skc_family != AF_PACKET)
		return 1;

	if (sk->sk_kern_sock)
		return 1;

	bpf_probe_read_kernel(&sa, sizeof(sa), address);
	if (sa.sll_protocol)
		return 0; /* EPERM */

	/* Can access cgroup local storage. */
	if (!test_local_storage())
		return 0; /* EPERM */

	return 1;
}

/* __cgroup_bpf_run_lsm_socket */
SEC("lsm_cgroup/socket_bind")
int BPF_PROG(socket_bind, struct socket *sock, struct sockaddr *address,
	     int addrlen)
{
	called_socket_bind++;
	return real_bind(sock, address, addrlen);
}

/* __cgroup_bpf_run_lsm_socket */
SEC("lsm_cgroup/socket_bind")
int BPF_PROG(socket_bind2, struct socket *sock, struct sockaddr *address,
	     int addrlen)
{
	called_socket_bind2++;
	return real_bind(sock, address, addrlen);
}

/* __cgroup_bpf_run_lsm_current (via bpf_lsm_current_hooks) */
SEC("lsm_cgroup/sk_alloc_security")
int BPF_PROG(socket_alloc, struct sock *sk, int family, gfp_t priority)
{
	called_socket_alloc++;
	/* if already have non-bpf lsms installed, EPERM will cause memory leak of non-bpf lsms */
	if (CONFIG_SECURITY_SELINUX || CONFIG_SECURITY_SMACK || CONFIG_SECURITY_APPARMOR)
		return 1;

	if (family == AF_UNIX)
		return 0; /* EPERM */

	/* Can access cgroup local storage. */
	if (!test_local_storage())
		return 0; /* EPERM */

	return 1;
}

/* __cgroup_bpf_run_lsm_sock */
SEC("lsm_cgroup/inet_csk_clone")
int BPF_PROG(socket_clone, struct sock *newsk, const struct request_sock *req)
{
	int prio = 234;

	if (!newsk)
		return 1;

	/* Accepted request sockets get a different priority. */
	if (bpf_setsockopt(newsk, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)))
		return 1;

	/* Make sure bpf_getsockopt is allowed and works. */
	prio = 0;
	if (bpf_getsockopt(newsk, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)))
		return 1;
	if (prio != 234)
		return 1;

	/* Can access cgroup local storage. */
	if (!test_local_storage())
		return 1;

	called_socket_clone++;

	return 1;
}