]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-capability.c
Merge pull request #16424 from keszybz/cap-bpf-compat
[thirdparty/systemd.git] / src / test / test-capability.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <netinet/in.h>
4 #include <pwd.h>
5 #include <sys/prctl.h>
6 #include <sys/socket.h>
7 #include <sys/wait.h>
8 #include <unistd.h>
9
10 #include "alloc-util.h"
11 #include "capability-util.h"
12 #include "errno-util.h"
13 #include "fd-util.h"
14 #include "fileio.h"
15 #include "macro.h"
16 #include "missing_prctl.h"
17 #include "parse-util.h"
18 #include "string-util.h"
19 #include "tests.h"
20
21 static uid_t test_uid = -1;
22 static gid_t test_gid = -1;
23
24 #if HAS_FEATURE_ADDRESS_SANITIZER
25 /* Keep CAP_SYS_PTRACE when running under Address Sanitizer */
26 static const uint64_t test_flags = UINT64_C(1) << CAP_SYS_PTRACE;
27 #else
28 /* We keep CAP_DAC_OVERRIDE to avoid errors with gcov when doing test coverage */
29 static const uint64_t test_flags = UINT64_C(1) << CAP_DAC_OVERRIDE;
30 #endif
31
32 /* verify cap_last_cap() against /proc/sys/kernel/cap_last_cap */
33 static void test_last_cap_file(void) {
34 _cleanup_free_ char *content = NULL;
35 unsigned long val = 0;
36 int r;
37
38 r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
39 if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r)) /* kernel pre 3.2 or no access */
40 return;
41 assert_se(r >= 0);
42
43 r = safe_atolu(content, &val);
44 assert_se(r >= 0);
45 assert_se(val != 0);
46 assert_se(val == cap_last_cap());
47 }
48
49 /* verify cap_last_cap() against syscall probing */
50 static void test_last_cap_probe(void) {
51 unsigned long p = (unsigned long)CAP_LAST_CAP;
52
53 if (prctl(PR_CAPBSET_READ, p) < 0) {
54 for (p--; p > 0; p --)
55 if (prctl(PR_CAPBSET_READ, p) >= 0)
56 break;
57 } else {
58 for (;; p++)
59 if (prctl(PR_CAPBSET_READ, p+1) < 0)
60 break;
61 }
62
63 assert_se(p != 0);
64 assert_se(p == cap_last_cap());
65 }
66
67 static void fork_test(void (*test_func)(void)) {
68 pid_t pid = 0;
69
70 pid = fork();
71 assert_se(pid >= 0);
72 if (pid == 0) {
73 test_func();
74 exit(EXIT_SUCCESS);
75 } else if (pid > 0) {
76 int status;
77
78 assert_se(waitpid(pid, &status, 0) > 0);
79 assert_se(WIFEXITED(status) && WEXITSTATUS(status) == 0);
80 }
81 }
82
83 static void show_capabilities(void) {
84 cap_t caps;
85 char *text;
86
87 caps = cap_get_proc();
88 assert_se(caps);
89
90 text = cap_to_text(caps, NULL);
91 assert_se(text);
92
93 log_info("Capabilities:%s", text);
94 cap_free(caps);
95 cap_free(text);
96 }
97
98 static int setup_tests(bool *run_ambient) {
99 struct passwd *nobody;
100 int r;
101
102 nobody = getpwnam(NOBODY_USER_NAME);
103 if (!nobody)
104 return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Could not find nobody user: %m");
105
106 test_uid = nobody->pw_uid;
107 test_gid = nobody->pw_gid;
108
109 *run_ambient = false;
110
111 r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
112
113 /* There's support for PR_CAP_AMBIENT if the prctl() call
114 * succeeded or error code was something else than EINVAL. The
115 * EINVAL check should be good enough to rule out false
116 * positives. */
117
118 if (r >= 0 || errno != EINVAL)
119 *run_ambient = true;
120
121 return 0;
122 }
123
124 static void test_drop_privileges_keep_net_raw(void) {
125 int sock;
126
127 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
128 assert_se(sock >= 0);
129 safe_close(sock);
130
131 assert_se(drop_privileges(test_uid, test_gid, test_flags | (1ULL << CAP_NET_RAW)) >= 0);
132 assert_se(getuid() == test_uid);
133 assert_se(getgid() == test_gid);
134 show_capabilities();
135
136 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
137 assert_se(sock >= 0);
138 safe_close(sock);
139 }
140
141 static void test_drop_privileges_dontkeep_net_raw(void) {
142 int sock;
143
144 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
145 assert_se(sock >= 0);
146 safe_close(sock);
147
148 assert_se(drop_privileges(test_uid, test_gid, test_flags) >= 0);
149 assert_se(getuid() == test_uid);
150 assert_se(getgid() == test_gid);
151 show_capabilities();
152
153 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
154 assert_se(sock < 0);
155 }
156
157 static void test_drop_privileges_fail(void) {
158 assert_se(drop_privileges(test_uid, test_gid, test_flags) >= 0);
159 assert_se(getuid() == test_uid);
160 assert_se(getgid() == test_gid);
161
162 assert_se(drop_privileges(test_uid, test_gid, test_flags) < 0);
163 assert_se(drop_privileges(0, 0, test_flags) < 0);
164 }
165
166 static void test_drop_privileges(void) {
167 fork_test(test_drop_privileges_keep_net_raw);
168 fork_test(test_drop_privileges_dontkeep_net_raw);
169 fork_test(test_drop_privileges_fail);
170 }
171
172 static void test_have_effective_cap(void) {
173 assert_se(have_effective_cap(CAP_KILL));
174 assert_se(have_effective_cap(CAP_CHOWN));
175
176 assert_se(drop_privileges(test_uid, test_gid, test_flags | (1ULL << CAP_KILL)) >= 0);
177 assert_se(getuid() == test_uid);
178 assert_se(getgid() == test_gid);
179
180 assert_se(have_effective_cap(CAP_KILL));
181 assert_se(!have_effective_cap(CAP_CHOWN));
182 }
183
184 static void test_update_inherited_set(void) {
185 cap_t caps;
186 uint64_t set = 0;
187 cap_flag_value_t fv;
188
189 caps = cap_get_proc();
190 assert_se(caps);
191
192 set = (UINT64_C(1) << CAP_CHOWN);
193
194 assert_se(!capability_update_inherited_set(caps, set));
195 assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
196 assert(fv == CAP_SET);
197
198 cap_free(caps);
199 }
200
201 static void test_apply_ambient_caps(void) {
202 cap_t caps;
203 uint64_t set = 0;
204 cap_flag_value_t fv;
205
206 assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0);
207
208 set = (UINT64_C(1) << CAP_CHOWN);
209
210 assert_se(!capability_ambient_set_apply(set, true));
211
212 caps = cap_get_proc();
213 assert_se(caps);
214 assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
215 assert_se(fv == CAP_SET);
216 cap_free(caps);
217
218 assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 1);
219
220 assert_se(!capability_ambient_set_apply(0, true));
221 caps = cap_get_proc();
222 assert_se(caps);
223 assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
224 assert_se(fv == CAP_CLEAR);
225 cap_free(caps);
226
227 assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0);
228 }
229
230 static void test_ensure_cap_64bit(void) {
231 _cleanup_free_ char *content = NULL;
232 unsigned long p = 0;
233 int r;
234
235 r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
236 if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r)) /* kernel pre 3.2 or no access */
237 return;
238 assert_se(r >= 0);
239
240 assert_se(safe_atolu(content, &p) >= 0);
241
242 /* If caps don't fit into 64bit anymore, we have a problem, fail the test. */
243 assert_se(p <= 63);
244
245 /* Also check for the header definition */
246 assert_cc(CAP_LAST_CAP <= 63);
247 }
248
249 int main(int argc, char *argv[]) {
250 bool run_ambient;
251
252 test_setup_logging(LOG_INFO);
253
254 test_ensure_cap_64bit();
255
256 test_last_cap_file();
257 test_last_cap_probe();
258
259 log_info("have ambient caps: %s", yes_no(ambient_capabilities_supported()));
260
261 if (getuid() != 0)
262 return log_tests_skipped("not running as root");
263
264 if (setup_tests(&run_ambient) < 0)
265 return log_tests_skipped("setup failed");
266
267 show_capabilities();
268
269 test_drop_privileges();
270 test_update_inherited_set();
271
272 fork_test(test_have_effective_cap);
273
274 if (run_ambient)
275 fork_test(test_apply_ambient_caps);
276
277 return 0;
278 }