Home | History | Annotate | Download | only in filecaps
      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 
     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
     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 
     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 
     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"
    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 
    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 
    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 
    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 
    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 
    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