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