xref: /linux/tools/testing/selftests/net/af_unix/scm_rights_denial_lsm.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <sys/socket.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 
13 #include <bpf/bpf.h>
14 #include <bpf/libbpf.h>
15 
16 #include "kselftest_harness.h"
17 
18 #ifndef SO_RIGHTS_NOTRUNC
19 #define SO_RIGHTS_NOTRUNC 85
20 #endif
21 
22 #define NR_FILES 2
23 
24 /* Per-file content, so a received fd can be matched to the file sent */
25 #define SECRET(n) "secret %d", (n)
26 
27 /* Indices into the socketpair */
28 #define SK_SENDER 0
29 #define SK_RECEIVER 1
30 
FIXTURE(scm_rights_denial_bpf)31 FIXTURE(scm_rights_denial_bpf)
32 {
33 	struct bpf_object *obj;
34 	struct bpf_link *link;
35 	int map_fd;
36 	int sk[2];
37 	int files[NR_FILES];
38 	__u64 inos[NR_FILES];
39 	char paths[NR_FILES][64];
40 };
41 
FIXTURE_VARIANT(scm_rights_denial_bpf)42 FIXTURE_VARIANT(scm_rights_denial_bpf)
43 {
44 	int sock_type;
45 };
46 
FIXTURE_VARIANT_ADD(scm_rights_denial_bpf,stream)47 FIXTURE_VARIANT_ADD(scm_rights_denial_bpf, stream)
48 {
49 	.sock_type = SOCK_STREAM,
50 };
51 
FIXTURE_VARIANT_ADD(scm_rights_denial_bpf,dgram)52 FIXTURE_VARIANT_ADD(scm_rights_denial_bpf, dgram)
53 {
54 	.sock_type = SOCK_DGRAM,
55 };
56 
FIXTURE_VARIANT_ADD(scm_rights_denial_bpf,seqpacket)57 FIXTURE_VARIANT_ADD(scm_rights_denial_bpf, seqpacket)
58 {
59 	.sock_type = SOCK_SEQPACKET,
60 };
61 
FIXTURE_SETUP(scm_rights_denial_bpf)62 FIXTURE_SETUP(scm_rights_denial_bpf)
63 {
64 	struct bpf_program *prog;
65 	char lsms[256] = {};
66 	int i, fd;
67 
68 	if (geteuid() != 0)
69 		SKIP(return, "requires root");
70 
71 	fd = open("/sys/kernel/security/lsm", O_RDONLY);
72 	ASSERT_GE(fd, 0);
73 	ASSERT_LT(0, read(fd, lsms, sizeof(lsms) - 1));
74 	close(fd);
75 
76 	if (!strstr(lsms, "bpf"))
77 		SKIP(return, "BPF LSM not active (boot with lsm=...,bpf)");
78 
79 	self->obj = bpf_object__open_file("scm_rights_denial_lsm.bpf.o", NULL);
80 	ASSERT_NE(NULL, self->obj);
81 	ASSERT_EQ(0, bpf_object__load(self->obj));
82 
83 	prog = bpf_object__find_program_by_name(self->obj, "scm_rights_deny");
84 	ASSERT_NE(NULL, prog);
85 
86 	self->link = bpf_program__attach_lsm(prog);
87 	ASSERT_NE(NULL, self->link);
88 
89 	self->map_fd = bpf_object__find_map_fd_by_name(self->obj,
90 						       "denied_inodes");
91 	ASSERT_GE(self->map_fd, 0);
92 
93 	ASSERT_EQ(0, socketpair(AF_UNIX, variant->sock_type, 0, self->sk));
94 
95 	for (i = 0; i < NR_FILES; i++) {
96 		struct stat st;
97 
98 		snprintf(self->paths[i], sizeof(self->paths[i]),
99 			 "/tmp/scm_rights_denial_bpf.%d.XXXXXX", i);
100 		self->files[i] = mkstemp(self->paths[i]);
101 		ASSERT_GE(self->files[i], 0);
102 
103 		ASSERT_LT(0, dprintf(self->files[i], SECRET(i)));
104 
105 		ASSERT_EQ(0, fstat(self->files[i], &st));
106 		self->inos[i] = st.st_ino;
107 	}
108 }
109 
FIXTURE_TEARDOWN(scm_rights_denial_bpf)110 FIXTURE_TEARDOWN(scm_rights_denial_bpf)
111 {
112 	bpf_link__destroy(self->link);
113 	bpf_object__close(self->obj);
114 
115 	for (int i = 0; i < NR_FILES; i++) {
116 		if (self->files[i] >= 0) {
117 			close(self->files[i]);
118 			unlink(self->paths[i]);
119 		}
120 	}
121 
122 	close(self->sk[SK_SENDER]);
123 	close(self->sk[SK_RECEIVER]);
124 }
125 
deny_inode(int map_fd,__u64 ino)126 static int deny_inode(int map_fd, __u64 ino)
127 {
128 	__u32 tgid = getpid();
129 
130 	return bpf_map_update_elem(map_fd, &ino, &tgid, BPF_ANY);
131 }
132 
set_notrunc(int sk)133 static int set_notrunc(int sk)
134 {
135 	int one = 1;
136 
137 	return setsockopt(sk, SOL_SOCKET, SO_RIGHTS_NOTRUNC,
138 			  &one, sizeof(one));
139 }
140 
send_fds(int sk,int * fds,int n)141 static int send_fds(int sk, int *fds, int n)
142 {
143 	char ctrl[CMSG_SPACE(NR_FILES * sizeof(int))] = {};
144 	char data = 'x';
145 	struct iovec iov = {
146 		.iov_base = &data,
147 		.iov_len = sizeof(data),
148 	};
149 	struct msghdr msg = {
150 		.msg_iov = &iov,
151 		.msg_iovlen = 1,
152 		.msg_control = ctrl,
153 		.msg_controllen = CMSG_SPACE(n * sizeof(int)),
154 	};
155 	struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
156 	int ret;
157 
158 	cmsg->cmsg_level = SOL_SOCKET;
159 	cmsg->cmsg_type = SCM_RIGHTS;
160 	cmsg->cmsg_len = CMSG_LEN(n * sizeof(int));
161 	memcpy(CMSG_DATA(cmsg), fds, n * sizeof(int));
162 
163 	ret = sendmsg(sk, &msg, 0);
164 	if (ret != 1)
165 		return -1;
166 
167 	return 0;
168 }
169 
recv_fd_slots(int sk,int * slots,int * msg_flags)170 static int recv_fd_slots(int sk, int *slots, int *msg_flags)
171 {
172 	int nr_slots;
173 	char ctrl[CMSG_SPACE(NR_FILES * sizeof(int))];
174 	char data;
175 	struct iovec iov = {
176 		.iov_base = &data,
177 		.iov_len = sizeof(data),
178 	};
179 	struct msghdr msg = {
180 		.msg_iov = &iov,
181 		.msg_iovlen = 1,
182 		.msg_control = ctrl,
183 		.msg_controllen = sizeof(ctrl),
184 	};
185 	struct cmsghdr *cmsg;
186 
187 	if (recvmsg(sk, &msg, 0) < 0)
188 		return -1;
189 
190 	*msg_flags = msg.msg_flags;
191 
192 	cmsg = CMSG_FIRSTHDR(&msg);
193 	if (!cmsg)
194 		return 0;
195 
196 	nr_slots = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
197 	memcpy(slots, CMSG_DATA(cmsg), nr_slots * sizeof(int));
198 
199 	return nr_slots;
200 }
201 
202 /* Prove a received fd works by reading back the file's content. */
check_secret(int fd,int idx)203 static int check_secret(int fd, int idx)
204 {
205 	char want[32], got[32] = {};
206 
207 	snprintf(want, sizeof(want), SECRET(idx));
208 	if (pread(fd, got, sizeof(got) - 1, 0) < 0)
209 		return -1;
210 
211 	return strcmp(want, got);
212 }
213 
TEST_F(scm_rights_denial_bpf,all_allowed)214 TEST_F(scm_rights_denial_bpf, all_allowed)
215 {
216 	int slots[NR_FILES], nr_slots, flags;
217 
218 	ASSERT_EQ(0, set_notrunc(self->sk[SK_RECEIVER]));
219 	ASSERT_EQ(0, send_fds(self->sk[SK_SENDER], self->files, NR_FILES));
220 	nr_slots = recv_fd_slots(self->sk[SK_RECEIVER], slots, &flags);
221 
222 	ASSERT_EQ(NR_FILES, nr_slots);
223 	EXPECT_EQ(0, flags & MSG_CTRUNC);
224 
225 	for (int i = 0; i < nr_slots; i++) {
226 		ASSERT_GE(slots[i], 0);
227 		EXPECT_EQ(0, check_secret(slots[i], i));
228 		close(slots[i]);
229 	}
230 }
231 
TEST_F(scm_rights_denial_bpf,first_denied)232 TEST_F(scm_rights_denial_bpf, first_denied)
233 {
234 	int slots[NR_FILES], nr_slots, flags;
235 
236 	ASSERT_EQ(0, deny_inode(self->map_fd, self->inos[0]));
237 
238 	ASSERT_EQ(0, set_notrunc(self->sk[SK_RECEIVER]));
239 	ASSERT_EQ(0, send_fds(self->sk[SK_SENDER], self->files, NR_FILES));
240 	nr_slots = recv_fd_slots(self->sk[SK_RECEIVER], slots, &flags);
241 
242 	ASSERT_EQ(NR_FILES, nr_slots);
243 	EXPECT_EQ(0, flags & MSG_CTRUNC);
244 
245 	EXPECT_EQ(-EPERM, slots[0]);
246 	for (int i = 1; i < nr_slots; i++) {
247 		ASSERT_GE(slots[i], 0);
248 		EXPECT_EQ(0, check_secret(slots[i], i));
249 		close(slots[i]);
250 	}
251 }
252 
TEST_F(scm_rights_denial_bpf,all_denied)253 TEST_F(scm_rights_denial_bpf, all_denied)
254 {
255 	int slots[NR_FILES], nr_slots, flags, i;
256 
257 	for (i = 0; i < NR_FILES; i++)
258 		ASSERT_EQ(0, deny_inode(self->map_fd, self->inos[i]));
259 
260 	ASSERT_EQ(0, set_notrunc(self->sk[SK_RECEIVER]));
261 	ASSERT_EQ(0, send_fds(self->sk[SK_SENDER], self->files, NR_FILES));
262 	nr_slots = recv_fd_slots(self->sk[SK_RECEIVER], slots, &flags);
263 
264 	ASSERT_EQ(NR_FILES, nr_slots);
265 	EXPECT_EQ(0, flags & MSG_CTRUNC);
266 
267 	for (i = 0; i < nr_slots; i++)
268 		EXPECT_EQ(-EPERM, slots[i]);
269 }
270 
TEST_F(scm_rights_denial_bpf,denied_without_notrunc)271 TEST_F(scm_rights_denial_bpf, denied_without_notrunc)
272 {
273 	int slots[NR_FILES], nr_slots, flags;
274 
275 	/*
276 	 * Baseline behaviour without SO_RIGHTS_NOTRUNC: the fd array is
277 	 * truncated at the first denied fd and MSG_CTRUNC is set.
278 	 */
279 	ASSERT_EQ(0, deny_inode(self->map_fd, self->inos[1]));
280 
281 	ASSERT_EQ(0, send_fds(self->sk[SK_SENDER], self->files, NR_FILES));
282 	nr_slots = recv_fd_slots(self->sk[SK_RECEIVER], slots, &flags);
283 
284 	ASSERT_EQ(1, nr_slots);
285 	EXPECT_NE(0, flags & MSG_CTRUNC);
286 
287 	ASSERT_GE(slots[0], 0);
288 	EXPECT_EQ(0, check_secret(slots[0], 0));
289 	close(slots[0]);
290 }
291 
292 TEST_HARNESS_MAIN
293