• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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