1/*
2 * filecap.c - A program that lists running processes with capabilities
3 * Copyright (c) 2009-10 Red Hat Inc., Durham, North Carolina.
4 * All Rights Reserved.
5 *
6 * This software may be freely redistributed and/or modified under the
7 * terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; see the file COPYING. If not, write to the
18 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * Authors:
21 *   Steve Grubb <sgrubb@redhat.com>
22 */
23
24#include "config.h"
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <errno.h>
29#include "cap-ng.h"
30#define __USE_GNU 1
31#include <fcntl.h>
32#define __USE_XOPEN_EXTENDED 1
33#include <ftw.h>
34
35static int show_all = 0, header = 0, capabilities = 0, cremove = 0;
36
37static void usage(void)
38{
39	fprintf(stderr,
40	    "usage: filecap [-a | -d | /dir | /dir/file [cap1 cap2 ...] ]\n");
41	exit(1);
42}
43
44static int check_file(const char *fpath,
45		const struct stat *sb,
46		int typeflag_unused __attribute__ ((unused)),
47		struct FTW *s_unused __attribute__ ((unused)))
48{
49	if (S_ISREG(sb->st_mode) == 0)
50		return FTW_CONTINUE;
51
52	int fd = open(fpath, O_RDONLY|O_CLOEXEC);
53	if (fd >= 0) {
54		capng_results_t rc;
55
56		capng_clear(CAPNG_SELECT_BOTH);
57		capng_get_caps_fd(fd);
58		rc = capng_have_capabilities(CAPNG_SELECT_CAPS);
59		if (rc > CAPNG_NONE) {
60			if (header == 0) {
61				header = 1;
62				printf("%-20s capabilities\n", "file");
63			}
64			printf("%s     ", fpath);
65			if (rc == CAPNG_FULL)
66				printf("full");
67			else
68				capng_print_caps_text(CAPNG_PRINT_STDOUT,
69						CAPNG_PERMITTED);
70			printf("\n");
71		}
72		close(fd);
73	}
74	return FTW_CONTINUE;
75}
76
77
78// Use cases:
79//  filecap
80//  filecap -a
81//  filecap /path/dir
82//  filecap /path/file
83//  filecap /path/file capability1 capability2 capability 3 ...
84//
85int main(int argc, char *argv[])
86{
87#if CAP_LAST_CAP < 31 || !defined (VFS_CAP_U32) || !defined (HAVE_ATTR_XATTR_H)
88	printf("File based capabilities are not supported\n");
89#else
90	char *path_env, *path = NULL, *dir = NULL;
91	struct stat sbuf;
92	int nftw_flags = FTW_PHYS;
93	int i;
94
95	if (argc >1) {
96		for (i=1; i<argc; i++) {
97			if (strcmp(argv[i], "-a") == 0) {
98				show_all = 1;
99				if (argc != 2)
100					usage();
101			} else if (strcmp(argv[i], "-d") == 0) {
102				for (i=0; i<=CAP_LAST_CAP; i++) {
103					const char *n =
104						capng_capability_to_name(i);
105					if (n == NULL)
106						n = "unknown";
107					printf("%s\n", n);
108				}
109				return 0;
110			} else if (argv[i][0] == '/') {
111				if (lstat(argv[i], &sbuf) != 0) {
112					printf("Error checking path %s (%s)\n",
113						argv[i], strerror(errno));
114					exit(1);
115				}
116				// Clear all capabilities in case cap strings
117				// follow. If we get a second file we err out
118				// so this is safe
119				if (S_ISREG(sbuf.st_mode) && path == NULL &&
120								 dir == NULL) {
121					path = argv[i];
122					capng_clear(CAPNG_SELECT_BOTH);
123				} else if (S_ISDIR(sbuf.st_mode) && path == NULL
124								&& dir == NULL)
125					dir = argv[i];
126				else {
127					printf("Must be one regular file or "
128						"directory\n");
129					exit(1);
130				}
131			} else {
132				int cap = capng_name_to_capability(argv[i]);
133				if (cap >= 0) {
134					if (path == NULL)
135						usage();
136					capng_update(CAPNG_ADD,
137						CAPNG_PERMITTED|CAPNG_EFFECTIVE,
138						cap);
139					capabilities = 1;
140				} else if (strcmp("none", argv[i]) == 0) {
141					capng_clear(CAPNG_SELECT_BOTH);
142					capabilities = 1;
143					cremove = 1;
144				} else {
145					printf("Unrecognized capability.\n");
146					usage();
147				}
148			}
149		}
150	}
151	if (path == NULL && dir == NULL && show_all == 0) {
152		path_env = getenv("PATH");
153		if (path_env != NULL) {
154			path = strdup(path_env);
155			for (dir=strtok(path,":"); dir!=NULL;
156						dir=strtok(NULL,":")) {
157				nftw(dir, check_file, 1024, nftw_flags);
158			}
159			free(path);
160		}
161	} else if (path == NULL && dir == NULL && show_all == 1) {
162		// Find files
163		nftw("/", check_file, 1024, nftw_flags);
164	} else if (dir) {
165		// Print out the dir
166		nftw(dir, check_file, 1024, nftw_flags);
167	}else if (path && capabilities == 0) {
168		// Print out specific file
169		check_file(path, &sbuf, 0, NULL);
170	} else if (path && capabilities == 1) {
171		// Write capabilities to file
172		int fd = open(path, O_WRONLY|O_NOFOLLOW|O_CLOEXEC);
173		if (fd < 0) {
174			printf("Could not open %s for writing (%s)\n", path,
175				strerror(errno));
176			return 1;
177		}
178		capng_apply_caps_fd(fd);
179		close(fd);
180	}
181#endif
182	return 0;
183}
184
185