]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-capability.c
Merge pull request #7567 from yuwata/fix-nobody
[thirdparty/systemd.git] / src / test / test-capability.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
21 #include <netinet/in.h>
22 #include <pwd.h>
23 #include <sys/capability.h>
24 #include <sys/prctl.h>
25 #include <sys/socket.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28
29 #include "alloc-util.h"
30 #include "capability-util.h"
31 #include "fd-util.h"
32 #include "fileio.h"
33 #include "macro.h"
34 #include "parse-util.h"
35 #include "util.h"
36
37 static uid_t test_uid = -1;
38 static gid_t test_gid = -1;
39
40 /* We keep CAP_DAC_OVERRIDE to avoid errors with gcov when doing test coverage */
41 static uint64_t test_flags = 1ULL << CAP_DAC_OVERRIDE;
42
43 /* verify cap_last_cap() against /proc/sys/kernel/cap_last_cap */
44 static 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 */
59 static 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
76 static 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
92 static 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
107 static int setup_tests(bool *run_ambient) {
108 struct passwd *nobody;
109 int r;
110
111 nobody = getpwnam(NOBODY_USER_NAME);
112 if (!nobody) {
113 log_error_errno(errno, "Could not find nobody user: %m");
114 return -EXIT_TEST_SKIP;
115 }
116 test_uid = nobody->pw_uid;
117 test_gid = nobody->pw_gid;
118
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
131 return 0;
132 }
133
134 static 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
151 static 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
167 static 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
176 static 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
182 static 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
194 static 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
213 static 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
238 int main(int argc, char *argv[]) {
239 int r;
240 bool run_ambient;
241
242 test_last_cap_file();
243 test_last_cap_probe();
244
245 log_parse_environment();
246 log_open();
247
248 log_info("have ambient caps: %s", yes_no(ambient_capabilities_supported()));
249
250 if (getuid() != 0)
251 return EXIT_TEST_SKIP;
252
253 r = setup_tests(&run_ambient);
254 if (r < 0)
255 return -r;
256
257 show_capabilities();
258
259 test_drop_privileges();
260 test_update_inherited_set();
261
262 fork_test(test_have_effective_cap);
263
264 if (run_ambient)
265 fork_test(test_set_ambient_caps);
266
267 return 0;
268 }