11d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich/*
21d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * filecap.c - A program that lists running processes with capabilities
31d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * Copyright (c) 2009-10 Red Hat Inc., Durham, North Carolina.
41d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * All Rights Reserved.
51d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich *
61d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * This software may be freely redistributed and/or modified under the
71d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * terms of the GNU General Public License as published by the Free
81d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * Software Foundation; either version 2, or (at your option) any
91d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * later version.
101d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich *
111d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * This program is distributed in the hope that it will be useful,
121d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * but WITHOUT ANY WARRANTY; without even the implied warranty of
131d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
141d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * GNU General Public License for more details.
151d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich *
161d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * You should have received a copy of the GNU General Public License
171d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * along with this program; see the file COPYING. If not, write to the
181d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
191d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich *
201d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich * Authors:
211d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich *   Steve Grubb <sgrubb@redhat.com>
221d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich */
231d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich
241d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#include "config.h"
251d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#include <stdio.h>
261d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#include <stdlib.h>
271d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#include <string.h>
281d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#include <errno.h>
291d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#include "cap-ng.h"
301d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#define __USE_GNU 1
311d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#include <fcntl.h>
321d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#define __USE_XOPEN_EXTENDED 1
331d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#include <ftw.h>
341d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich
351d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevichstatic int show_all = 0, header = 0, capabilities = 0, cremove = 0;
361d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich
371d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevichstatic void usage(void)
381d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich{
391d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	fprintf(stderr,
401d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	    "usage: filecap [-a | -d | /dir | /dir/file [cap1 cap2 ...] ]\n");
411d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	exit(1);
421d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich}
431d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich
441d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevichstatic int check_file(const char *fpath,
451d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		const struct stat *sb,
461d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		int typeflag_unused __attribute__ ((unused)),
471d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		struct FTW *s_unused __attribute__ ((unused)))
481d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich{
491d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	if (S_ISREG(sb->st_mode) == 0)
501d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		return FTW_CONTINUE;
511d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich
521d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	int fd = open(fpath, O_RDONLY|O_CLOEXEC);
531d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	if (fd >= 0) {
541d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		capng_results_t rc;
551d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich
561d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		capng_clear(CAPNG_SELECT_BOTH);
571d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		capng_get_caps_fd(fd);
581d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		rc = capng_have_capabilities(CAPNG_SELECT_CAPS);
591d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		if (rc > CAPNG_NONE) {
601d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			if (header == 0) {
611d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				header = 1;
621d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				printf("%-20s capabilities\n", "file");
631d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			}
641d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			printf("%s     ", fpath);
651d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			if (rc == CAPNG_FULL)
661d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				printf("full");
671d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			else
681d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				capng_print_caps_text(CAPNG_PRINT_STDOUT,
691d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich						CAPNG_PERMITTED);
701d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			printf("\n");
711d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		}
721d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		close(fd);
731d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	}
741d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	return FTW_CONTINUE;
751d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich}
761d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich
771d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich
781d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich// Use cases:
791d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich//  filecap
801d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich//  filecap -a
811d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich//  filecap /path/dir
821d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich//  filecap /path/file
831d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich//  filecap /path/file capability1 capability2 capability 3 ...
841d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich//
851d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevichint main(int argc, char *argv[])
861d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich{
871d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#if CAP_LAST_CAP < 31 || !defined (VFS_CAP_U32) || !defined (HAVE_ATTR_XATTR_H)
881d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	printf("File based capabilities are not supported\n");
891d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#else
901d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	char *path_env, *path = NULL, *dir = NULL;
911d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	struct stat sbuf;
921d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	int nftw_flags = FTW_PHYS;
931d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	int i;
941d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich
951d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	if (argc >1) {
961d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		for (i=1; i<argc; i++) {
971d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			if (strcmp(argv[i], "-a") == 0) {
981d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				show_all = 1;
991d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				if (argc != 2)
1001d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					usage();
1011d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			} else if (strcmp(argv[i], "-d") == 0) {
1021d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				for (i=0; i<=CAP_LAST_CAP; i++) {
1031d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					const char *n =
1041d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich						capng_capability_to_name(i);
1051d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					if (n == NULL)
1061d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich						n = "unknown";
1071d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					printf("%s\n", n);
1081d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				}
1091d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				return 0;
1101d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			} else if (argv[i][0] == '/') {
1111d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				if (lstat(argv[i], &sbuf) != 0) {
1121d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					printf("Error checking path %s (%s)\n",
1131d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich						argv[i], strerror(errno));
1141d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					exit(1);
1151d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				}
1161d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				// Clear all capabilities in case cap strings
1171d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				// follow. If we get a second file we err out
1181d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				// so this is safe
1191d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				if (S_ISREG(sbuf.st_mode) && path == NULL &&
1201d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich								 dir == NULL) {
1211d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					path = argv[i];
1221d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					capng_clear(CAPNG_SELECT_BOTH);
1231d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				} else if (S_ISDIR(sbuf.st_mode) && path == NULL
1241d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich								&& dir == NULL)
1251d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					dir = argv[i];
1261d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				else {
1271d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					printf("Must be one regular file or "
1281d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich						"directory\n");
1291d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					exit(1);
1301d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				}
1311d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			} else {
1321d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				int cap = capng_name_to_capability(argv[i]);
1331d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				if (cap >= 0) {
1341d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					if (path == NULL)
1351d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich						usage();
1361d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					capng_update(CAPNG_ADD,
1371d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich						CAPNG_PERMITTED|CAPNG_EFFECTIVE,
1381d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich						cap);
1391d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					capabilities = 1;
1401d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				} else if (strcmp("none", argv[i]) == 0) {
1411d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					capng_clear(CAPNG_SELECT_BOTH);
1421d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					capabilities = 1;
1431d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					cremove = 1;
1441d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				} else {
1451d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					printf("Unrecognized capability.\n");
1461d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich					usage();
1471d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				}
1481d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			}
1491d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		}
1501d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	}
1511d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	if (path == NULL && dir == NULL && show_all == 0) {
1521d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		path_env = getenv("PATH");
1531d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		if (path_env != NULL) {
1541d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			path = strdup(path_env);
1551d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			for (dir=strtok(path,":"); dir!=NULL;
1561d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich						dir=strtok(NULL,":")) {
1571d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				nftw(dir, check_file, 1024, nftw_flags);
1581d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			}
1591d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			free(path);
1601d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		}
1611d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	} else if (path == NULL && dir == NULL && show_all == 1) {
1621d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		// Find files
1631d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		nftw("/", check_file, 1024, nftw_flags);
1641d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	} else if (dir) {
1651d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		// Print out the dir
1661d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		nftw(dir, check_file, 1024, nftw_flags);
1671d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	}else if (path && capabilities == 0) {
1681d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		// Print out specific file
1691d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		check_file(path, &sbuf, 0, NULL);
1701d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	} else if (path && capabilities == 1) {
1711d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		// Write capabilities to file
1721d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		int fd = open(path, O_WRONLY|O_NOFOLLOW|O_CLOEXEC);
1731d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		if (fd < 0) {
1741d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			printf("Could not open %s for writing (%s)\n", path,
1751d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich				strerror(errno));
1761d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich			return 1;
1771d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		}
1781d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		capng_apply_caps_fd(fd);
1791d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich		close(fd);
1801d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	}
1811d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich#endif
1821d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich	return 0;
1831d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich}
1841d1011a3c5049a7f9eef99d22f3704e4367579ccNick Kralevich
185