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\n");
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\n");
92 ret = cap_set_proc(cap);
93 if (ret < 0)
94 tst_brkm(TBROK | TERRNO, NULL, "cap_set_proc failed\n");
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\n");
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\n");
113 ret = 0;
114 } else {
115 tst_resm(TFAIL, "could set capabilities as non-root\n");
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\n",
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\n");
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\n", __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\n", __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\n");
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..\n",
199 capstxt, p);
200 tst_resm(TINFO, "those are not the same\n");
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 ret = prctl(PR_CAPBSET_READ, num_caps);
230 /*
231 * Break from the loop in this manner to avoid incrementing,
232 * then having to decrement value.
233 */
234 if (ret == -1)
235 break;
236 }
237
238 /* first, try each bit in fP (forced) with fE on and off. */
239 for (whichcap = 0; whichcap < num_caps; whichcap++) {
240 /*
241 * fP=whichcap, fE=fI=0
242 * pP'=whichcap, pI'=pE'=0
243 */
244 capvalue[0] = whichcap;
245 cap_clear(fcap);
246 cap_set_flag(fcap, CAP_PERMITTED, 1, capvalue, CAP_SET);
247 ret = cap_set_file(TSTPATH, fcap);
248 if (ret) {
249 tst_resm(TINFO, "%d\n", whichcap);
250 continue;
251 }
252 ret = fork_drop_and_exec(DROP_PERMS, fcap);
253 if (ret) {
254 tst_resm(TINFO,
255 "Failed CAP_PERMITTED=%d CAP_EFFECTIVE=0\n",
256 whichcap);
257 if (!finalret)
258 finalret = ret;
259 }
260
261 /* SERGE here */
262 /*
263 * fP = fE = whichcap, fI = 0
264 * pP = pE = whichcap, pI = 0
265 */
266 cap_clear(fcap);
267 cap_set_flag(fcap, CAP_PERMITTED, 1, capvalue, CAP_SET);
268 cap_set_flag(fcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET);
269 ret = cap_set_file(TSTPATH, fcap);
270 if (ret) {
271 tst_resm(TINFO, "%d\n", whichcap);
272 continue;
273 }
274 ret = fork_drop_and_exec(DROP_PERMS, fcap);
275 if (ret) {
276 tst_resm(TINFO,
277 "Failed CAP_PERMITTED=%d CAP_EFFECTIVE=1\n",
278 whichcap);
279 if (!finalret)
280 finalret = ret;
281 }
282 }
283
284 cap_free(pcap);
285 cap_free(fcap);
286 cap_fullpi = cap_init();
287 for (i = 0; i < num_caps; i++) {
288 capvalue[0] = i;
289 cap_set_flag(cap_fullpi, CAP_INHERITABLE, 1, capvalue, CAP_SET);
290 }
291
292 /*
293 * For the inheritable tests, we want to make sure pI starts
294 * filled.
295 */
296 ret = cap_set_proc(cap_fullpi);
297 if (ret)
298 tst_resm(TINFO, "Could not fill pI. pI tests will fail.\n");
299
300 /*
301 * next try each bit in fI
302 * The first two attemps have the bit which is in fI in pI.
303 * This should result in the bit being in pP'.
304 * If fE was set then it should also be in pE'.
305 * The last attempt starts with an empty pI.
306 * This should result in empty capability, as there were
307 * no bits to be inherited from the original process.
308 */
309 for (whichcap = 0; whichcap < num_caps; whichcap++) {
310 cap_t cmpcap;
311 capvalue[0] = whichcap;
312
313 /*
314 * fI=whichcap, fP=fE=0
315 * pI=full
316 * pI'=full, pP'=whichcap, pE'=0
317 */
318 /* fill pI' */
319 pcap = cap_dup(cap_fullpi);
320 /* pP' = whichcap */
321 cap_set_flag(pcap, CAP_PERMITTED, 1, capvalue, CAP_SET);
322
323 /* fI = whichcap */
324 fcap = cap_init();
325 cap_set_flag(fcap, CAP_INHERITABLE, 1, capvalue, CAP_SET);
326 ret = cap_set_file(TSTPATH, fcap);
327 if (ret) {
328 tst_resm(TINFO, "%d\n", whichcap);
329 continue;
330 }
331 ret = fork_drop_and_exec(KEEP_PERMS, pcap);
332 if (ret) {
333 tst_resm(TINFO, "Failed with_perms CAP_INHERITABLE=%d "
334 "CAP_EFFECTIVE=0\n", whichcap);
335 if (!finalret)
336 finalret = ret;
337 }
338
339 /*
340 * fI=fE=whichcap, fP=0
341 * pI=full
342 * pI'=full, pP'=whichcap, pE'=whichcap
343 *
344 * Note that only fE and pE' change, so keep prior
345 * fcap and pcap and set those bits.
346 */
347
348 cap_set_flag(fcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET);
349 cap_set_flag(pcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET);
350 ret = cap_set_file(TSTPATH, fcap);
351 if (ret) {
352 tst_resm(TINFO, "%d\n", whichcap);
353 continue;
354 }
355 /* The actual result will be a full pI, with
356 * pE and pP containing just whichcap. */
357 cmpcap = cap_dup(cap_fullpi);
358 cap_set_flag(cmpcap, CAP_PERMITTED, 1, capvalue, CAP_SET);
359 cap_set_flag(cmpcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET);
360 ret = fork_drop_and_exec(KEEP_PERMS, cmpcap);
361 cap_free(cmpcap);
362 if (ret) {
363 tst_resm(TINFO, "Failed with_perms CAP_INHERITABLE=%d "
364 "CAP_EFFECTIVE=1\n", whichcap);
365 if (!finalret)
366 finalret = ret;
367 }
368
369 /*
370 * fI=fE=whichcap, fP=0 (so fcap is same as before)
371 * pI=0 (achieved using DROP_PERMS)
372 * pI'=pP'=pE'=0
373 */
374 cap_clear(pcap);
375 ret = fork_drop_and_exec(DROP_PERMS, pcap);
376 if (ret) {
377 tst_resm(TINFO,
378 "Failed without_perms CAP_INHERITABLE=%d",
379 whichcap);
380 if (!finalret)
381 finalret = ret;
382 }
383
384 cap_free(fcap);
385 cap_free(pcap);
386 }
387
388 cap_free(cap_fullpi);
389
390 return finalret;
391 }
392 #endif
393
main(int argc,char * argv[])394 int main(int argc, char *argv[])
395 {
396 #ifdef HAVE_LIBCAP
397 if (argc < 2)
398 usage(argv[0]);
399
400 int ret = 0;
401
402 switch (atoi(argv[1])) {
403 case 0:
404 ret = perms_test();
405 break;
406 case 1:
407 ret = caps_actually_set_test();
408 if (ret)
409 tst_resm(TFAIL, "Some tests failed\n");
410 else
411 tst_resm(TPASS, "All tests passed\n");
412 break;
413 default:
414 usage(argv[0]);
415 }
416 #else
417 tst_resm(TCONF, "System doesn't have POSIX capabilities support.");
418 #endif
419
420 tst_exit();
421 }
422