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