]> git.proxmox.com Git - systemd.git/blame - src/test/test-capability.c
New upstream version 240
[systemd.git] / src / test / test-capability.c
CommitLineData
52ad194e 1/* SPDX-License-Identifier: LGPL-2.1+ */
e842803a 2
e842803a
MB
3#include <netinet/in.h>
4#include <pwd.h>
db2df898 5#include <sys/capability.h>
4c89c718 6#include <sys/prctl.h>
db2df898
MP
7#include <sys/socket.h>
8#include <sys/wait.h>
e842803a
MB
9#include <unistd.h>
10
52ad194e 11#include "alloc-util.h"
db2df898
MP
12#include "capability-util.h"
13#include "fd-util.h"
52ad194e 14#include "fileio.h"
e842803a 15#include "macro.h"
52ad194e 16#include "parse-util.h"
6e866b33 17#include "tests.h"
db2df898 18#include "util.h"
e842803a
MB
19
20static uid_t test_uid = -1;
21static gid_t test_gid = -1;
db2df898 22
6e866b33
MB
23#if HAS_FEATURE_ADDRESS_SANITIZER
24/* Keep CAP_SYS_PTRACE when running under Address Sanitizer */
25static const uint64_t test_flags = UINT64_C(1) << CAP_SYS_PTRACE;
26#else
db2df898 27/* We keep CAP_DAC_OVERRIDE to avoid errors with gcov when doing test coverage */
6e866b33
MB
28static const uint64_t test_flags = UINT64_C(1) << CAP_DAC_OVERRIDE;
29#endif
e842803a 30
52ad194e
MB
31/* verify cap_last_cap() against /proc/sys/kernel/cap_last_cap */
32static 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 */
47static 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
e842803a
MB
64static 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();
1d42b86d 71 exit(EXIT_SUCCESS);
e842803a
MB
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
80static 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
4c89c718 95static int setup_tests(bool *run_ambient) {
e842803a 96 struct passwd *nobody;
4c89c718 97 int r;
e842803a 98
52ad194e 99 nobody = getpwnam(NOBODY_USER_NAME);
6e866b33
MB
100 if (!nobody)
101 return log_error_errno(errno, "Could not find nobody user: %m");
102
e842803a
MB
103 test_uid = nobody->pw_uid;
104 test_gid = nobody->pw_gid;
105
4c89c718
MP
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
e842803a
MB
118 return 0;
119}
120
121static 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
138static 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
154static 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
163static 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
169static 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
4c89c718
MP
181static 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);
4c89c718
MP
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
198static void test_set_ambient_caps(void) {
199 cap_t caps;
200 uint64_t set = 0;
201 cap_flag_value_t fv;
202
4c89c718
MP
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
e842803a 217int main(int argc, char *argv[]) {
4c89c718 218 bool run_ambient;
e842803a 219
6e866b33
MB
220 test_setup_logging(LOG_INFO);
221
52ad194e
MB
222 test_last_cap_file();
223 test_last_cap_probe();
224
f5e65279
MB
225 log_info("have ambient caps: %s", yes_no(ambient_capabilities_supported()));
226
e842803a 227 if (getuid() != 0)
6e866b33 228 return log_tests_skipped("not running as root");
e842803a 229
6e866b33
MB
230 if (setup_tests(&run_ambient) < 0)
231 return log_tests_skipped("setup failed");
e842803a
MB
232
233 show_capabilities();
234
235 test_drop_privileges();
4c89c718
MP
236 test_update_inherited_set();
237
e842803a
MB
238 fork_test(test_have_effective_cap);
239
4c89c718
MP
240 if (run_ambient)
241 fork_test(test_set_ambient_caps);
242
e842803a
MB
243 return 0;
244}