1/*
2 * pscap.c - A program that lists running processes with capabilities
3 * Copyright (c) 2009,2012 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 <stdio_ext.h>
27#include <unistd.h>
28#include <stdlib.h>
29#include <errno.h>
30#include <string.h>
31#include <dirent.h>
32#include <fcntl.h>
33#include <pwd.h>
34#include "cap-ng.h"
35
36
37static void usage(void)
38{
39	fprintf(stderr, "usage: pscap [-a]\n");
40	exit(1);
41}
42
43int main(int argc, char *argv[])
44{
45	DIR *d;
46	struct dirent *ent;
47	int header = 0, show_all = 0, caps;
48	pid_t our_pid = getpid();
49
50	if (argc > 2) {
51		fputs("Too many arguments\n", stderr);
52		usage();
53	}
54	if (argc == 2) {
55		if (strcmp(argv[1], "-a") == 0)
56			show_all = 1;
57		else
58			usage();
59	}
60
61	d = opendir("/proc");
62	if (d == NULL) {
63		printf("Can't open /proc: %s\n", strerror(errno));
64		return 1;
65	}
66	while (( ent = readdir(d) )) {
67		int pid, ppid, uid = -1, euid = -1;
68		char buf[100];
69		char *tmp, cmd[16], state, *name = NULL;
70		int fd, len;
71		struct passwd *p;
72
73		// Skip non-process dir entries
74		if(*ent->d_name<'0' || *ent->d_name>'9')
75			continue;
76		errno = 0;
77		pid = strtol(ent->d_name, NULL, 10);
78		if (errno)
79			continue;
80
81		/* Skip our pid so we aren't listed */
82		if (pid == our_pid)
83			continue;
84
85		// Parse up the stat file for the proc
86		snprintf(buf, 32, "/proc/%d/stat", pid);
87		fd = open(buf, O_RDONLY|O_CLOEXEC, 0);
88		if (fd < 0)
89			continue;
90		len = read(fd, buf, sizeof buf - 1);
91		close(fd);
92		if (len < 40)
93			continue;
94		buf[len] = 0;
95		tmp = strrchr(buf, ')');
96		if (tmp)
97			*tmp = 0;
98		else
99			continue;
100		memset(cmd, 0, sizeof(cmd));
101		sscanf(buf, "%d (%15c", &ppid, cmd);
102		sscanf(tmp+2, "%c %d", &state, &ppid);
103
104		// Skip kthreads
105		if (pid == 2 || ppid == 2)
106			continue;
107
108		if (!show_all && pid == 1)
109			continue;
110
111		// now get the capabilities
112		capng_clear(CAPNG_SELECT_BOTH);
113		capng_setpid(pid);
114		if (capng_get_caps_process())
115			continue;
116
117		// And print out anything with capabilities
118		caps = capng_have_capabilities(CAPNG_SELECT_CAPS);
119		if (caps > CAPNG_NONE) {
120			// Get the effective uid
121			FILE *f;
122			int line;
123			snprintf(buf, 32, "/proc/%d/status", pid);
124			f = fopen(buf, "rte");
125			if (f == NULL)
126				euid = 0;
127			else {
128				line = 0;
129				__fsetlocking(f, FSETLOCKING_BYCALLER);
130				while (fgets(buf, sizeof(buf), f)) {
131					if (line == 0) {
132						line++;
133						continue;
134					}
135					if (memcmp(buf, "Uid:", 4) == 0) {
136						int id;
137						sscanf(buf, "Uid: %d %d",
138							&id, &euid);
139						break;
140					}
141				}
142				fclose(f);
143			}
144
145			len = read(fd, buf, sizeof buf - 1);
146			close(fd);
147			if (header == 0) {
148				printf("%-5s %-5s %-10s  %-16s  %s\n",
149				    "ppid", "pid", "name", "command",
150				    "capabilities");
151				header = 1;
152			}
153			if (euid == 0) {
154				// Take short cut for this one
155				name = "root";
156				uid = 0;
157			} else if (euid != uid) {
158				// Only look up if name changed
159				p = getpwuid(euid);
160				uid = euid;
161				if (p)
162					name = p->pw_name;
163				// If not taking this branch, use last val
164			}
165			if (name) {
166				printf("%-5d %-5d %-10s  %-16s  ", ppid, pid,
167					name, cmd);
168			} else
169				printf("%-5d %-5d %-10d  %-16s  ", ppid, pid,
170					uid, cmd);
171			if (caps == CAPNG_PARTIAL) {
172				capng_print_caps_text(CAPNG_PRINT_STDOUT,
173							CAPNG_PERMITTED);
174				if (capng_have_capabilities(CAPNG_SELECT_BOUNDS)
175							 == CAPNG_FULL)
176					printf(" +");
177				printf("\n");
178			} else
179				printf("full\n");
180		}
181	}
182	closedir(d);
183	return 0;
184}
185
186