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