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