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