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