1 /******************************************************************************/
2 /* */
3 /* Copyright (c) International Business Machines Corp., 2007, 2008 */
4 /* */
5 /* This program is free software; you can redistribute it and/or modify */
6 /* it under the terms of the GNU General Public License as published by */
7 /* the Free Software Foundation; either version 2 of the License, or */
8 /* (at your option) any later version. */
9 /* */
10 /* This program is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
13 /* the GNU General Public License for more details. */
14 /* */
15 /* You should have received a copy of the GNU General Public License */
16 /* along with this program; if not, write to the Free Software */
17 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
18 /* */
19 /******************************************************************************/
20 /*
21 * File: verify_caps_exec.c
22 * Author: Serge Hallyn
23 * Purpose: perform several tests of file capabilities:
24 * 1. try setting caps without privilege
25 * 2. test proper calculation of pI', pE', and pP'.
26 * Try setting valid caps, drop rights, and run the executable,
27 * make sure we get the rights
28 */
29
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <endian.h>
33 #include <byteswap.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/wait.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include "config.h"
40 #if HAVE_SYS_CAPABILITY_H
41 #include <linux/types.h>
42 #include <sys/capability.h>
43 #endif
44 #include <sys/prctl.h>
45 #include "test.h"
46 #include "filecaps_common.h"
47
48 #define TSTPATH "print_caps"
49 char *TCID = "filecaps";
50 int TST_TOTAL = 1;
51
52 int errno;
53
usage(const char * me)54 static void usage(const char *me)
55 {
56 tst_resm(TFAIL, "Usage: %s <0|1> [arg]\n", me);
57 tst_resm(TINFO, " 0: set file caps without privilege\n");
58 tst_resm(TINFO, " 1: test that file caps are set correctly on exec\n");
59 tst_exit();
60 }
61
62 #define DROP_PERMS 0
63 #define KEEP_PERMS 1
64
65 #ifdef HAVE_LIBCAP
print_my_caps(void)66 static void print_my_caps(void)
67 {
68 cap_t cap = cap_get_proc();
69 char *txt = cap_to_text(cap, NULL);
70 tst_resm(TINFO, "\ncaps are %s\n", txt);
71 cap_free(cap);
72 cap_free(txt);
73 }
74
drop_root(int keep_perms)75 static void drop_root(int keep_perms)
76 {
77 int ret;
78
79 if (keep_perms)
80 prctl(PR_SET_KEEPCAPS, 1);
81 ret = setresuid(1000, 1000, 1000);
82 if (ret) {
83 tst_brkm(TFAIL | TERRNO, NULL, "Error dropping root privs");
84 tst_exit();
85 }
86 if (keep_perms) {
87 cap_t cap = cap_from_text("=eip");
88 int ret;
89 if (!cap)
90 tst_brkm(TBROK | TERRNO, NULL,
91 "cap_from_text failed");
92 ret = cap_set_proc(cap);
93 if (ret < 0)
94 tst_brkm(TBROK | TERRNO, NULL, "cap_set_proc failed");
95 cap_free(cap);
96 }
97 }
98
perms_test(void)99 static int perms_test(void)
100 {
101 int ret;
102 cap_t cap;
103
104 drop_root(DROP_PERMS);
105 cap = cap_from_text("all=eip");
106 if (!cap) {
107 tst_resm(TFAIL, "could not get cap from text for perms test");
108 return 1;
109 }
110 ret = cap_set_file(TSTPATH, cap);
111 if (ret) {
112 tst_resm(TPASS, "could not set capabilities as non-root");
113 ret = 0;
114 } else {
115 tst_resm(TFAIL, "could set capabilities as non-root");
116 ret = 1;
117 }
118
119 cap_free(cap);
120 return ret;
121 }
122
create_fifo(void)123 static void create_fifo(void)
124 {
125 int ret;
126
127 ret = mkfifo(get_caps_fifo(), S_IRWXU | S_IRWXG | S_IRWXO);
128 if (ret == -1 && errno != EEXIST)
129 tst_brkm(TFAIL | TERRNO, NULL, "failed creating %s",
130 get_caps_fifo());
131 }
132
write_to_fifo(const char * buf)133 static void write_to_fifo(const char *buf)
134 {
135 int fd;
136
137 fd = open(get_caps_fifo(), O_WRONLY);
138 write(fd, buf, strlen(buf));
139 close(fd);
140 }
141
read_from_fifo(char * buf)142 static void read_from_fifo(char *buf)
143 {
144 int fd;
145
146 memset(buf, 0, 200);
147 fd = open(get_caps_fifo(), O_RDONLY);
148 if (fd < 0)
149 tst_brkm(TFAIL | TERRNO, NULL, "Failed opening fifo");
150 read(fd, buf, 199);
151 close(fd);
152 }
153
fork_drop_and_exec(int keepperms,cap_t expected_caps)154 static int fork_drop_and_exec(int keepperms, cap_t expected_caps)
155 {
156
157 int pid;
158 int ret = 0;
159 char buf[200], *p;
160 char *capstxt;
161 cap_t actual_caps;
162 static int seqno;
163
164 pid = fork();
165 if (pid < 0)
166 tst_brkm(TFAIL | TERRNO, NULL, "%s: failed fork", __func__);
167 if (pid == 0) {
168 drop_root(keepperms);
169 print_my_caps();
170 sprintf(buf, "%d", seqno);
171 ret = execlp(TSTPATH, TSTPATH, buf, NULL);
172 capstxt = cap_to_text(expected_caps, NULL);
173 snprintf(buf, 200, "failed to run as %s\n", capstxt);
174 cap_free(capstxt);
175 write_to_fifo(buf);
176 tst_brkm(TFAIL, NULL, "%s: exec failed", __func__);
177 } else {
178 p = buf;
179 while (1) {
180 int c, s;
181 read_from_fifo(buf);
182 c = sscanf(buf, "%d", &s);
183 if (c == 1 && s == seqno)
184 break;
185 tst_resm(TINFO,
186 "got a bad seqno (c=%d, s=%d, seqno=%d)", c, s,
187 seqno);
188 }
189 p = strchr(buf, '.');
190 if (!p)
191 tst_brkm(TFAIL, NULL,
192 "got a bad message from print_caps");
193 p += 1;
194 actual_caps = cap_from_text(p);
195 if (cap_compare(actual_caps, expected_caps) != 0) {
196 capstxt = cap_to_text(expected_caps, NULL);
197 tst_resm(TINFO,
198 "Expected to run as .%s., ran as .%s..",
199 capstxt, p);
200 tst_resm(TINFO, "those are not the same");
201 cap_free(capstxt);
202 ret = -1;
203 }
204 cap_free(actual_caps);
205 seqno++;
206 }
207 return ret;
208 }
209
caps_actually_set_test(void)210 static int caps_actually_set_test(void)
211 {
212 int whichcap, finalret = 0, ret;
213 cap_t fcap, pcap, cap_fullpi;
214 cap_value_t capvalue[1];
215 int i;
216
217 fcap = cap_init();
218 pcap = cap_init();
219 if (!fcap || !pcap) {
220 perror("cap_init");
221 exit(2);
222 }
223
224 create_fifo();
225
226 int num_caps;
227
228 for (num_caps = 0;; num_caps++) {
229 #if HAVE_DECL_PR_CAPBSET_READ
230 ret = prctl(PR_CAPBSET_READ, num_caps);
231 #else
232 tst_resm(TCONF, "System doesn't have CAPBSET prctls");
233 ret = -1;
234 #endif
235 /*
236 * Break from the loop in this manner to avoid incrementing,
237 * then having to decrement value.
238 */
239 if (ret == -1)
240 break;
241 }
242
243 /* first, try each bit in fP (forced) with fE on and off. */
244 for (whichcap = 0; whichcap < num_caps; whichcap++) {
245 /*
246 * fP=whichcap, fE=fI=0
247 * pP'=whichcap, pI'=pE'=0
248 */
249 capvalue[0] = whichcap;
250 cap_clear(fcap);
251 cap_set_flag(fcap, CAP_PERMITTED, 1, capvalue, CAP_SET);
252 ret = cap_set_file(TSTPATH, fcap);
253 if (ret) {
254 tst_resm(TINFO, "%d", whichcap);
255 continue;
256 }
257 ret = fork_drop_and_exec(DROP_PERMS, fcap);
258 if (ret) {
259 tst_resm(TINFO,
260 "Failed CAP_PERMITTED=%d CAP_EFFECTIVE=0",
261 whichcap);
262 if (!finalret)
263 finalret = ret;
264 }
265
266 /* SERGE here */
267 /*
268 * fP = fE = whichcap, fI = 0
269 * pP = pE = whichcap, pI = 0
270 */
271 cap_clear(fcap);
272 cap_set_flag(fcap, CAP_PERMITTED, 1, capvalue, CAP_SET);
273 cap_set_flag(fcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET);
274 ret = cap_set_file(TSTPATH, fcap);
275 if (ret) {
276 tst_resm(TINFO, "%d", whichcap);
277 continue;
278 }
279 ret = fork_drop_and_exec(DROP_PERMS, fcap);
280 if (ret) {
281 tst_resm(TINFO,
282 "Failed CAP_PERMITTED=%d CAP_EFFECTIVE=1",
283 whichcap);
284 if (!finalret)
285 finalret = ret;
286 }
287 }
288
289 cap_free(pcap);
290 cap_free(fcap);
291 cap_fullpi = cap_init();
292 for (i = 0; i < num_caps; i++) {
293 capvalue[0] = i;
294 cap_set_flag(cap_fullpi, CAP_INHERITABLE, 1, capvalue, CAP_SET);
295 }
296
297 /*
298 * For the inheritable tests, we want to make sure pI starts
299 * filled.
300 */
301 ret = cap_set_proc(cap_fullpi);
302 if (ret)
303 tst_resm(TINFO, "Could not fill pI. pI tests will fail.");
304
305 /*
306 * next try each bit in fI
307 * The first two attemps have the bit which is in fI in pI.
308 * This should result in the bit being in pP'.
309 * If fE was set then it should also be in pE'.
310 * The last attempt starts with an empty pI.
311 * This should result in empty capability, as there were
312 * no bits to be inherited from the original process.
313 */
314 for (whichcap = 0; whichcap < num_caps; whichcap++) {
315 cap_t cmpcap;
316 capvalue[0] = whichcap;
317
318 /*
319 * fI=whichcap, fP=fE=0
320 * pI=full
321 * pI'=full, pP'=whichcap, pE'=0
322 */
323 /* fill pI' */
324 pcap = cap_dup(cap_fullpi);
325 /* pP' = whichcap */
326 cap_set_flag(pcap, CAP_PERMITTED, 1, capvalue, CAP_SET);
327
328 /* fI = whichcap */
329 fcap = cap_init();
330 cap_set_flag(fcap, CAP_INHERITABLE, 1, capvalue, CAP_SET);
331 ret = cap_set_file(TSTPATH, fcap);
332 if (ret) {
333 tst_resm(TINFO, "%d", whichcap);
334 continue;
335 }
336 ret = fork_drop_and_exec(KEEP_PERMS, pcap);
337 if (ret) {
338 tst_resm(TINFO, "Failed with_perms CAP_INHERITABLE=%d "
339 "CAP_EFFECTIVE=0", whichcap);
340 if (!finalret)
341 finalret = ret;
342 }
343
344 /*
345 * fI=fE=whichcap, fP=0
346 * pI=full
347 * pI'=full, pP'=whichcap, pE'=whichcap
348 *
349 * Note that only fE and pE' change, so keep prior
350 * fcap and pcap and set those bits.
351 */
352
353 cap_set_flag(fcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET);
354 cap_set_flag(pcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET);
355 ret = cap_set_file(TSTPATH, fcap);
356 if (ret) {
357 tst_resm(TINFO, "%d", whichcap);
358 continue;
359 }
360 /* The actual result will be a full pI, with
361 * pE and pP containing just whichcap. */
362 cmpcap = cap_dup(cap_fullpi);
363 cap_set_flag(cmpcap, CAP_PERMITTED, 1, capvalue, CAP_SET);
364 cap_set_flag(cmpcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET);
365 ret = fork_drop_and_exec(KEEP_PERMS, cmpcap);
366 cap_free(cmpcap);
367 if (ret) {
368 tst_resm(TINFO, "Failed with_perms CAP_INHERITABLE=%d "
369 "CAP_EFFECTIVE=1", whichcap);
370 if (!finalret)
371 finalret = ret;
372 }
373
374 /*
375 * fI=fE=whichcap, fP=0 (so fcap is same as before)
376 * pI=0 (achieved using DROP_PERMS)
377 * pI'=pP'=pE'=0
378 */
379 cap_clear(pcap);
380 ret = fork_drop_and_exec(DROP_PERMS, pcap);
381 if (ret) {
382 tst_resm(TINFO,
383 "Failed without_perms CAP_INHERITABLE=%d",
384 whichcap);
385 if (!finalret)
386 finalret = ret;
387 }
388
389 cap_free(fcap);
390 cap_free(pcap);
391 }
392
393 cap_free(cap_fullpi);
394
395 return finalret;
396 }
397 #endif
398
main(int argc,char * argv[])399 int main(int argc, char *argv[])
400 {
401 #ifdef HAVE_LIBCAP
402 if (argc < 2)
403 usage(argv[0]);
404
405 int ret = 0;
406
407 switch (atoi(argv[1])) {
408 case 0:
409 ret = perms_test();
410 break;
411 case 1:
412 ret = caps_actually_set_test();
413 if (ret)
414 tst_resm(TFAIL, "Some tests failed");
415 else
416 tst_resm(TPASS, "All tests passed");
417 break;
418 default:
419 usage(argv[0]);
420 }
421 #else
422 tst_resm(TCONF, "System doesn't have POSIX capabilities support.");
423 #endif
424
425 tst_exit();
426 }
427