]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-capability.c
Merge pull request #6918 from ssahani/issue-5625
[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 "capability-util.h"
30 #include "fd-util.h"
31 #include "macro.h"
32 #include "util.h"
33
34 static uid_t test_uid = -1;
35 static gid_t test_gid = -1;
36
37 /* We keep CAP_DAC_OVERRIDE to avoid errors with gcov when doing test coverage */
38 static uint64_t test_flags = 1ULL << CAP_DAC_OVERRIDE;
39
40 static void fork_test(void (*test_func)(void)) {
41 pid_t pid = 0;
42
43 pid = fork();
44 assert_se(pid >= 0);
45 if (pid == 0) {
46 test_func();
47 exit(0);
48 } else if (pid > 0) {
49 int status;
50
51 assert_se(waitpid(pid, &status, 0) > 0);
52 assert_se(WIFEXITED(status) && WEXITSTATUS(status) == 0);
53 }
54 }
55
56 static void show_capabilities(void) {
57 cap_t caps;
58 char *text;
59
60 caps = cap_get_proc();
61 assert_se(caps);
62
63 text = cap_to_text(caps, NULL);
64 assert_se(text);
65
66 log_info("Capabilities:%s", text);
67 cap_free(caps);
68 cap_free(text);
69 }
70
71 static int setup_tests(bool *run_ambient) {
72 struct passwd *nobody;
73 int r;
74
75 nobody = getpwnam("nobody");
76 if (!nobody) {
77 log_error_errno(errno, "Could not find nobody user: %m");
78 return -EXIT_TEST_SKIP;
79 }
80 test_uid = nobody->pw_uid;
81 test_gid = nobody->pw_gid;
82
83 *run_ambient = false;
84
85 r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
86
87 /* There's support for PR_CAP_AMBIENT if the prctl() call
88 * succeeded or error code was something else than EINVAL. The
89 * EINVAL check should be good enough to rule out false
90 * positives. */
91
92 if (r >= 0 || errno != EINVAL)
93 *run_ambient = true;
94
95 return 0;
96 }
97
98 static void test_drop_privileges_keep_net_raw(void) {
99 int sock;
100
101 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
102 assert_se(sock >= 0);
103 safe_close(sock);
104
105 assert_se(drop_privileges(test_uid, test_gid, test_flags | (1ULL << CAP_NET_RAW)) >= 0);
106 assert_se(getuid() == test_uid);
107 assert_se(getgid() == test_gid);
108 show_capabilities();
109
110 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
111 assert_se(sock >= 0);
112 safe_close(sock);
113 }
114
115 static void test_drop_privileges_dontkeep_net_raw(void) {
116 int sock;
117
118 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
119 assert_se(sock >= 0);
120 safe_close(sock);
121
122 assert_se(drop_privileges(test_uid, test_gid, test_flags) >= 0);
123 assert_se(getuid() == test_uid);
124 assert_se(getgid() == test_gid);
125 show_capabilities();
126
127 sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
128 assert_se(sock < 0);
129 }
130
131 static void test_drop_privileges_fail(void) {
132 assert_se(drop_privileges(test_uid, test_gid, test_flags) >= 0);
133 assert_se(getuid() == test_uid);
134 assert_se(getgid() == test_gid);
135
136 assert_se(drop_privileges(test_uid, test_gid, test_flags) < 0);
137 assert_se(drop_privileges(0, 0, test_flags) < 0);
138 }
139
140 static void test_drop_privileges(void) {
141 fork_test(test_drop_privileges_keep_net_raw);
142 fork_test(test_drop_privileges_dontkeep_net_raw);
143 fork_test(test_drop_privileges_fail);
144 }
145
146 static void test_have_effective_cap(void) {
147 assert_se(have_effective_cap(CAP_KILL));
148 assert_se(have_effective_cap(CAP_CHOWN));
149
150 assert_se(drop_privileges(test_uid, test_gid, test_flags | (1ULL << CAP_KILL)) >= 0);
151 assert_se(getuid() == test_uid);
152 assert_se(getgid() == test_gid);
153
154 assert_se(have_effective_cap(CAP_KILL));
155 assert_se(!have_effective_cap(CAP_CHOWN));
156 }
157
158 static void test_update_inherited_set(void) {
159 cap_t caps;
160 uint64_t set = 0;
161 cap_flag_value_t fv;
162
163 caps = cap_get_proc();
164 assert_se(caps);
165 assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
166 assert(fv == CAP_CLEAR);
167
168 set = (UINT64_C(1) << CAP_CHOWN);
169
170 assert_se(!capability_update_inherited_set(caps, set));
171 assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
172 assert(fv == CAP_SET);
173
174 cap_free(caps);
175 }
176
177 static void test_set_ambient_caps(void) {
178 cap_t caps;
179 uint64_t set = 0;
180 cap_flag_value_t fv;
181
182 caps = cap_get_proc();
183 assert_se(caps);
184 assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
185 assert(fv == CAP_CLEAR);
186 cap_free(caps);
187
188 assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0);
189
190 set = (UINT64_C(1) << CAP_CHOWN);
191
192 assert_se(!capability_ambient_set_apply(set, true));
193
194 caps = cap_get_proc();
195 assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
196 assert(fv == CAP_SET);
197 cap_free(caps);
198
199 assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 1);
200 }
201
202 int main(int argc, char *argv[]) {
203 int r;
204 bool run_ambient;
205
206 log_parse_environment();
207 log_open();
208
209 log_info("have ambient caps: %s", yes_no(ambient_capabilities_supported()));
210
211 if (getuid() != 0)
212 return EXIT_TEST_SKIP;
213
214 r = setup_tests(&run_ambient);
215 if (r < 0)
216 return -r;
217
218 show_capabilities();
219
220 test_drop_privileges();
221 test_update_inherited_set();
222
223 fork_test(test_have_effective_cap);
224
225 if (run_ambient)
226 fork_test(test_set_ambient_caps);
227
228 return 0;
229 }