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"
49char *TCID = "filecaps";
50int TST_TOTAL = 1;
51
52int errno;
53
54static 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
66static 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
75static 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
99static 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
123static 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
133static 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
142static 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
154static 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
210static 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
399int 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