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