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 
47 #define TSTPATH "print_caps"
48 char *TCID = "filecaps";
49 int TST_TOTAL = 1;
50 
51 int errno;
52 
usage(const char * me)53 static void usage(const char *me)
54 {
55 	tst_resm(TFAIL, "Usage: %s <0|1> [arg]\n", me);
56 	tst_resm(TINFO, "  0: set file caps without privilege\n");
57 	tst_resm(TINFO, "  1: test that file caps are set correctly on exec\n");
58 	tst_exit();
59 }
60 
61 #define DROP_PERMS 0
62 #define KEEP_PERMS 1
63 
64 #ifdef HAVE_LIBCAP
print_my_caps(void)65 static void print_my_caps(void)
66 {
67 	cap_t cap = cap_get_proc();
68 	char *txt = cap_to_text(cap, NULL);
69 	tst_resm(TINFO, "\ncaps are %s\n", txt);
70 	cap_free(cap);
71 	cap_free(txt);
72 }
73 
drop_root(int keep_perms)74 static void drop_root(int keep_perms)
75 {
76 	int ret;
77 
78 	if (keep_perms)
79 		prctl(PR_SET_KEEPCAPS, 1);
80 	ret = setresuid(1000, 1000, 1000);
81 	if (ret) {
82 		tst_brkm(TFAIL | TERRNO, NULL, "Error dropping root privs\n");
83 		tst_exit();
84 	}
85 	if (keep_perms) {
86 		cap_t cap = cap_from_text("=eip");
87 		int ret;
88 		if (!cap)
89 			tst_brkm(TBROK | TERRNO, NULL,
90 				 "cap_from_text failed\n");
91 		ret = cap_set_proc(cap);
92 		if (ret < 0)
93 			tst_brkm(TBROK | TERRNO, NULL, "cap_set_proc failed\n");
94 		cap_free(cap);
95 	}
96 }
97 
perms_test(void)98 static int perms_test(void)
99 {
100 	int ret;
101 	cap_t cap;
102 
103 	drop_root(DROP_PERMS);
104 	cap = cap_from_text("all=eip");
105 	if (!cap) {
106 		tst_resm(TFAIL, "could not get cap from text for perms test\n");
107 		return 1;
108 	}
109 	ret = cap_set_file(TSTPATH, cap);
110 	if (ret) {
111 		tst_resm(TPASS, "could not set capabilities as non-root\n");
112 		ret = 0;
113 	} else {
114 		tst_resm(TFAIL, "could set capabilities as non-root\n");
115 		ret = 1;
116 	}
117 
118 	cap_free(cap);
119 	return ret;
120 }
121 
122 #define FIFOFILE "/tmp/caps_fifo"
create_fifo(void)123 static void create_fifo(void)
124 {
125 	int ret;
126 
127 	ret = mkfifo(FIFOFILE, S_IRWXU | S_IRWXG | S_IRWXO);
128 	if (ret == -1 && errno != EEXIST)
129 		tst_brkm(TFAIL | TERRNO, NULL, "failed creating %s\n",
130 			 FIFOFILE);
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(FIFOFILE, 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(FIFOFILE, 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 = index(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