]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-capability.c
Merge pull request #7554 from keszybz/autodetect-build
[thirdparty/systemd.git] / src / test / test-capability.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
6160e473
RC
2/***
3 This file is part of systemd
4
5 Copyright 2014 Ronny Chevalier
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
6160e473
RC
21#include <netinet/in.h>
22#include <pwd.h>
3ffd4af2 23#include <sys/capability.h>
70d7aea5 24#include <sys/prctl.h>
3ffd4af2
LP
25#include <sys/socket.h>
26#include <sys/wait.h>
6160e473
RC
27#include <unistd.h>
28
4c1a95fd 29#include "alloc-util.h"
430f0182 30#include "capability-util.h"
3ffd4af2 31#include "fd-util.h"
4c1a95fd 32#include "fileio.h"
6160e473 33#include "macro.h"
4c1a95fd 34#include "parse-util.h"
3ffd4af2 35#include "util.h"
6160e473
RC
36
37static uid_t test_uid = -1;
38static gid_t test_gid = -1;
3ffd4af2
LP
39
40/* We keep CAP_DAC_OVERRIDE to avoid errors with gcov when doing test coverage */
6160e473
RC
41static uint64_t test_flags = 1ULL << CAP_DAC_OVERRIDE;
42
4c1a95fd
YW
43/* verify cap_last_cap() against /proc/sys/kernel/cap_last_cap */
44static void test_last_cap_file(void) {
45 _cleanup_free_ char *content = NULL;
46 unsigned long val = 0;
47 int r;
48
49 r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
50 assert_se(r >= 0);
51
52 r = safe_atolu(content, &val);
53 assert_se(r >= 0);
54 assert_se(val != 0);
55 assert_se(val == cap_last_cap());
56}
57
58/* verify cap_last_cap() against syscall probing */
59static void test_last_cap_probe(void) {
60 unsigned long p = (unsigned long)CAP_LAST_CAP;
61
62 if (prctl(PR_CAPBSET_READ, p) < 0) {
63 for (p--; p > 0; p --)
64 if (prctl(PR_CAPBSET_READ, p) >= 0)
65 break;
66 } else {
67 for (;; p++)
68 if (prctl(PR_CAPBSET_READ, p+1) < 0)
69 break;
70 }
71
72 assert_se(p != 0);
73 assert_se(p == cap_last_cap());
74}
75
6160e473
RC
76static void fork_test(void (*test_func)(void)) {
77 pid_t pid = 0;
78
79 pid = fork();
80 assert_se(pid >= 0);
81 if (pid == 0) {
82 test_func();
83 exit(0);
84 } else if (pid > 0) {
85 int status;
86
87 assert_se(waitpid(pid, &status, 0) > 0);
88 assert_se(WIFEXITED(status) && WEXITSTATUS(status) == 0);
89 }
90}
91
92static void show_capabilities(void) {
93 cap_t caps;
94 char *text;
95
96 caps = cap_get_proc();
97 assert_se(caps);
98
99 text = cap_to_text(caps, NULL);
100 assert_se(text);
101
102 log_info("Capabilities:%s", text);
103 cap_free(caps);
104 cap_free(text);
105}
106
70d7aea5 107static int setup_tests(bool *run_ambient) {
6160e473 108 struct passwd *nobody;
70d7aea5 109 int r;
6160e473
RC
110
111 nobody = getpwnam("nobody");
112 if (!nobody) {
56f64d95 113 log_error_errno(errno, "Could not find nobody user: %m");
6160e473
RC
114 return -EXIT_TEST_SKIP;
115 }
116 test_uid = nobody->pw_uid;
117 test_gid = nobody->pw_gid;
118
70d7aea5
IP
119 *run_ambient = false;
120
121 r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
122
123 /* There's support for PR_CAP_AMBIENT if the prctl() call
124 * succeeded or error code was something else than EINVAL. The
125 * EINVAL check should be good enough to rule out false
126 * positives. */
127
128 if (r >= 0 || errno != EINVAL)
129 *run_ambient = true;
130
6160e473
RC
131 return 0;
132}
133
134static void test_drop_privileges_keep_net_raw(void) {
135 int sock;
136
137 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
138 assert_se(sock >= 0);
139 safe_close(sock);
140
141 assert_se(drop_privileges(test_uid, test_gid, test_flags | (1ULL << CAP_NET_RAW)) >= 0);
142 assert_se(getuid() == test_uid);
143 assert_se(getgid() == test_gid);
144 show_capabilities();
145
146 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
147 assert_se(sock >= 0);
148 safe_close(sock);
149}
150
151static void test_drop_privileges_dontkeep_net_raw(void) {
152 int sock;
153
154 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
155 assert_se(sock >= 0);
156 safe_close(sock);
157
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 show_capabilities();
162
163 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
164 assert_se(sock < 0);
165}
166
167static void test_drop_privileges_fail(void) {
168 assert_se(drop_privileges(test_uid, test_gid, test_flags) >= 0);
169 assert_se(getuid() == test_uid);
170 assert_se(getgid() == test_gid);
171
172 assert_se(drop_privileges(test_uid, test_gid, test_flags) < 0);
173 assert_se(drop_privileges(0, 0, test_flags) < 0);
174}
175
176static void test_drop_privileges(void) {
177 fork_test(test_drop_privileges_keep_net_raw);
178 fork_test(test_drop_privileges_dontkeep_net_raw);
179 fork_test(test_drop_privileges_fail);
180}
181
182static void test_have_effective_cap(void) {
183 assert_se(have_effective_cap(CAP_KILL));
184 assert_se(have_effective_cap(CAP_CHOWN));
185
186 assert_se(drop_privileges(test_uid, test_gid, test_flags | (1ULL << CAP_KILL)) >= 0);
187 assert_se(getuid() == test_uid);
188 assert_se(getgid() == test_gid);
189
190 assert_se(have_effective_cap(CAP_KILL));
191 assert_se(!have_effective_cap(CAP_CHOWN));
192}
193
70d7aea5
IP
194static void test_update_inherited_set(void) {
195 cap_t caps;
196 uint64_t set = 0;
197 cap_flag_value_t fv;
198
199 caps = cap_get_proc();
200 assert_se(caps);
201 assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
202 assert(fv == CAP_CLEAR);
203
204 set = (UINT64_C(1) << CAP_CHOWN);
205
206 assert_se(!capability_update_inherited_set(caps, set));
207 assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
208 assert(fv == CAP_SET);
209
210 cap_free(caps);
211}
212
213static void test_set_ambient_caps(void) {
214 cap_t caps;
215 uint64_t set = 0;
216 cap_flag_value_t fv;
217
218 caps = cap_get_proc();
219 assert_se(caps);
220 assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
221 assert(fv == CAP_CLEAR);
222 cap_free(caps);
223
224 assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0);
225
226 set = (UINT64_C(1) << CAP_CHOWN);
227
228 assert_se(!capability_ambient_set_apply(set, true));
229
230 caps = cap_get_proc();
231 assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
232 assert(fv == CAP_SET);
233 cap_free(caps);
234
235 assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 1);
236}
237
6160e473
RC
238int main(int argc, char *argv[]) {
239 int r;
70d7aea5 240 bool run_ambient;
6160e473 241
4c1a95fd
YW
242 test_last_cap_file();
243 test_last_cap_probe();
244
6160e473
RC
245 log_parse_environment();
246 log_open();
247
39f608e4
LP
248 log_info("have ambient caps: %s", yes_no(ambient_capabilities_supported()));
249
6160e473
RC
250 if (getuid() != 0)
251 return EXIT_TEST_SKIP;
252
70d7aea5 253 r = setup_tests(&run_ambient);
6160e473
RC
254 if (r < 0)
255 return -r;
256
257 show_capabilities();
258
259 test_drop_privileges();
70d7aea5
IP
260 test_update_inherited_set();
261
6160e473
RC
262 fork_test(test_have_effective_cap);
263
70d7aea5
IP
264 if (run_ambient)
265 fork_test(test_set_ambient_caps);
266
6160e473
RC
267 return 0;
268}