xref: /aosp_15_r20/external/libcap-ng/utils/pscap.c (revision 8dd5e09d5faf27a871e8654ddaa2d2af7c696578)
1*8dd5e09dSSadaf Ebrahimi /*
2*8dd5e09dSSadaf Ebrahimi  * pscap.c - A program that lists running processes with capabilities
3*8dd5e09dSSadaf Ebrahimi  * Copyright (c) 2009,2012,2020 Red Hat Inc.
4*8dd5e09dSSadaf Ebrahimi  * All Rights Reserved.
5*8dd5e09dSSadaf Ebrahimi  *
6*8dd5e09dSSadaf Ebrahimi  * This software may be freely redistributed and/or modified under the
7*8dd5e09dSSadaf Ebrahimi  * terms of the GNU General Public License as published by the Free
8*8dd5e09dSSadaf Ebrahimi  * Software Foundation; either version 2, or (at your option) any
9*8dd5e09dSSadaf Ebrahimi  * later version.
10*8dd5e09dSSadaf Ebrahimi  *
11*8dd5e09dSSadaf Ebrahimi  * This program is distributed in the hope that it will be useful,
12*8dd5e09dSSadaf Ebrahimi  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13*8dd5e09dSSadaf Ebrahimi  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*8dd5e09dSSadaf Ebrahimi  * GNU General Public License for more details.
15*8dd5e09dSSadaf Ebrahimi  *
16*8dd5e09dSSadaf Ebrahimi  * You should have received a copy of the GNU General Public License
17*8dd5e09dSSadaf Ebrahimi  * along with this program; see the file COPYING. If not, write to the
18*8dd5e09dSSadaf Ebrahimi  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
19*8dd5e09dSSadaf Ebrahimi  * Boston, MA 02110-1335, USA.
20*8dd5e09dSSadaf Ebrahimi  *
21*8dd5e09dSSadaf Ebrahimi  * Authors:
22*8dd5e09dSSadaf Ebrahimi  *   Steve Grubb <[email protected]>
23*8dd5e09dSSadaf Ebrahimi  */
24*8dd5e09dSSadaf Ebrahimi 
25*8dd5e09dSSadaf Ebrahimi #include "config.h"
26*8dd5e09dSSadaf Ebrahimi #include <stdio.h>
27*8dd5e09dSSadaf Ebrahimi #include <stdio_ext.h>
28*8dd5e09dSSadaf Ebrahimi #include <unistd.h>
29*8dd5e09dSSadaf Ebrahimi #include <stdlib.h>
30*8dd5e09dSSadaf Ebrahimi #include <errno.h>
31*8dd5e09dSSadaf Ebrahimi #include <string.h>
32*8dd5e09dSSadaf Ebrahimi #include <dirent.h>
33*8dd5e09dSSadaf Ebrahimi #include <fcntl.h>
34*8dd5e09dSSadaf Ebrahimi #include <pwd.h>
35*8dd5e09dSSadaf Ebrahimi #include <stdbool.h>
36*8dd5e09dSSadaf Ebrahimi #include <sys/stat.h>
37*8dd5e09dSSadaf Ebrahimi #include "cap-ng.h"
38*8dd5e09dSSadaf Ebrahimi 
39*8dd5e09dSSadaf Ebrahimi #define CMD_LEN 16
40*8dd5e09dSSadaf Ebrahimi #define USERNS_MARK_LEN 2
41*8dd5e09dSSadaf Ebrahimi 
usage(void)42*8dd5e09dSSadaf Ebrahimi static void usage(void)
43*8dd5e09dSSadaf Ebrahimi {
44*8dd5e09dSSadaf Ebrahimi 	fprintf(stderr, "usage: pscap [-a|-p pid]\n");
45*8dd5e09dSSadaf Ebrahimi 	exit(1);
46*8dd5e09dSSadaf Ebrahimi }
47*8dd5e09dSSadaf Ebrahimi 
48*8dd5e09dSSadaf Ebrahimi /*
49*8dd5e09dSSadaf Ebrahimi  * Precise recursive checks for parent-child relation between namespaces
50*8dd5e09dSSadaf Ebrahimi  * using ioctl() were avoided, because there didn't seem to be any case when
51*8dd5e09dSSadaf Ebrahimi  * we may dereference the namespace symlink in /proc/PID/ns for processes in
52*8dd5e09dSSadaf Ebrahimi  * user namespaces other than the current or child ones. Thus, the check just
53*8dd5e09dSSadaf Ebrahimi  * tries to dereference the link and checks that it does not point to the
54*8dd5e09dSSadaf Ebrahimi  * current NS.
55*8dd5e09dSSadaf Ebrahimi  */
in_child_userns(int pid)56*8dd5e09dSSadaf Ebrahimi static bool in_child_userns(int pid)
57*8dd5e09dSSadaf Ebrahimi {
58*8dd5e09dSSadaf Ebrahimi 	char ns_file_path[32];
59*8dd5e09dSSadaf Ebrahimi 	struct stat statbuf;
60*8dd5e09dSSadaf Ebrahimi 	ino_t own_ns_inode;
61*8dd5e09dSSadaf Ebrahimi 	dev_t own_ns_dev;
62*8dd5e09dSSadaf Ebrahimi 
63*8dd5e09dSSadaf Ebrahimi 	if (stat("/proc/self/ns/user", &statbuf) < 0)
64*8dd5e09dSSadaf Ebrahimi 		return false;
65*8dd5e09dSSadaf Ebrahimi 
66*8dd5e09dSSadaf Ebrahimi 	own_ns_inode = statbuf.st_ino;
67*8dd5e09dSSadaf Ebrahimi 	own_ns_dev = statbuf.st_dev;
68*8dd5e09dSSadaf Ebrahimi 
69*8dd5e09dSSadaf Ebrahimi 	snprintf(ns_file_path, 32, "/proc/%d/ns/user", pid);
70*8dd5e09dSSadaf Ebrahimi 	if (stat(ns_file_path, &statbuf) < 0)
71*8dd5e09dSSadaf Ebrahimi 		return false;
72*8dd5e09dSSadaf Ebrahimi 
73*8dd5e09dSSadaf Ebrahimi 	return statbuf.st_ino != own_ns_inode || statbuf.st_dev != own_ns_dev;
74*8dd5e09dSSadaf Ebrahimi }
75*8dd5e09dSSadaf Ebrahimi 
main(int argc,char * argv[])76*8dd5e09dSSadaf Ebrahimi int main(int argc, char *argv[])
77*8dd5e09dSSadaf Ebrahimi {
78*8dd5e09dSSadaf Ebrahimi 	char *endptr = NULL;
79*8dd5e09dSSadaf Ebrahimi 	DIR *d;
80*8dd5e09dSSadaf Ebrahimi 	struct dirent *ent;
81*8dd5e09dSSadaf Ebrahimi 	int header = 0, show_all = 0, caps;
82*8dd5e09dSSadaf Ebrahimi 	pid_t our_pid = getpid();
83*8dd5e09dSSadaf Ebrahimi 	pid_t target_pid = 0;
84*8dd5e09dSSadaf Ebrahimi 
85*8dd5e09dSSadaf Ebrahimi 	if (argc > 3) {
86*8dd5e09dSSadaf Ebrahimi 		fputs("Too many arguments\n", stderr);
87*8dd5e09dSSadaf Ebrahimi 		usage();
88*8dd5e09dSSadaf Ebrahimi 	}
89*8dd5e09dSSadaf Ebrahimi 	if (argc == 2) {
90*8dd5e09dSSadaf Ebrahimi 		if (strcmp(argv[1], "-a") == 0)
91*8dd5e09dSSadaf Ebrahimi 			show_all = 1;
92*8dd5e09dSSadaf Ebrahimi 		else
93*8dd5e09dSSadaf Ebrahimi 			usage();
94*8dd5e09dSSadaf Ebrahimi 	}
95*8dd5e09dSSadaf Ebrahimi 	else if (argc == 3) {
96*8dd5e09dSSadaf Ebrahimi 		if (strcmp(argv[1], "-p") == 0) {
97*8dd5e09dSSadaf Ebrahimi 			errno = 0;
98*8dd5e09dSSadaf Ebrahimi 			target_pid = strtol(argv[2], &endptr, 10);
99*8dd5e09dSSadaf Ebrahimi 			if (errno) {
100*8dd5e09dSSadaf Ebrahimi 				fprintf(stderr, "Can't read pid: %s\n", argv[2]);
101*8dd5e09dSSadaf Ebrahimi 				return 1;
102*8dd5e09dSSadaf Ebrahimi 			}
103*8dd5e09dSSadaf Ebrahimi 			if ((endptr == argv[2]) || (*endptr != '\0') || !target_pid) {
104*8dd5e09dSSadaf Ebrahimi 				fprintf(stderr, "Invalid pid argument: %s\n", argv[2]);
105*8dd5e09dSSadaf Ebrahimi 				return 1;
106*8dd5e09dSSadaf Ebrahimi 			}
107*8dd5e09dSSadaf Ebrahimi 			if (target_pid == 1)
108*8dd5e09dSSadaf Ebrahimi 				show_all = 1;
109*8dd5e09dSSadaf Ebrahimi 		} else
110*8dd5e09dSSadaf Ebrahimi 			usage();
111*8dd5e09dSSadaf Ebrahimi 	}
112*8dd5e09dSSadaf Ebrahimi 
113*8dd5e09dSSadaf Ebrahimi 	d = opendir("/proc");
114*8dd5e09dSSadaf Ebrahimi 	if (d == NULL) {
115*8dd5e09dSSadaf Ebrahimi 		fprintf(stderr, "Can't open /proc: %s\n", strerror(errno));
116*8dd5e09dSSadaf Ebrahimi 		return 1;
117*8dd5e09dSSadaf Ebrahimi 	}
118*8dd5e09dSSadaf Ebrahimi 	while (( ent = readdir(d) )) {
119*8dd5e09dSSadaf Ebrahimi 		int pid, ppid, uid = -1, euid = -1;
120*8dd5e09dSSadaf Ebrahimi 		char buf[100];
121*8dd5e09dSSadaf Ebrahimi 		char *tmp, cmd[CMD_LEN + USERNS_MARK_LEN], state, *name = NULL;
122*8dd5e09dSSadaf Ebrahimi 		int fd, len;
123*8dd5e09dSSadaf Ebrahimi 		struct passwd *p;
124*8dd5e09dSSadaf Ebrahimi 
125*8dd5e09dSSadaf Ebrahimi 		// Skip non-process dir entries
126*8dd5e09dSSadaf Ebrahimi 		if(*ent->d_name<'0' || *ent->d_name>'9')
127*8dd5e09dSSadaf Ebrahimi 			continue;
128*8dd5e09dSSadaf Ebrahimi 		errno = 0;
129*8dd5e09dSSadaf Ebrahimi 		pid = strtol(ent->d_name, NULL, 10);
130*8dd5e09dSSadaf Ebrahimi 		if (errno)
131*8dd5e09dSSadaf Ebrahimi 			continue;
132*8dd5e09dSSadaf Ebrahimi 
133*8dd5e09dSSadaf Ebrahimi 		if (target_pid && (pid != target_pid))
134*8dd5e09dSSadaf Ebrahimi 			continue;
135*8dd5e09dSSadaf Ebrahimi 
136*8dd5e09dSSadaf Ebrahimi 		/* Skip our pid so we aren't listed */
137*8dd5e09dSSadaf Ebrahimi 		if (pid == our_pid)
138*8dd5e09dSSadaf Ebrahimi 			continue;
139*8dd5e09dSSadaf Ebrahimi 
140*8dd5e09dSSadaf Ebrahimi 		// Parse up the stat file for the proc
141*8dd5e09dSSadaf Ebrahimi 		snprintf(buf, 32, "/proc/%d/stat", pid);
142*8dd5e09dSSadaf Ebrahimi 		fd = open(buf, O_RDONLY|O_CLOEXEC, 0);
143*8dd5e09dSSadaf Ebrahimi 		if (fd < 0)
144*8dd5e09dSSadaf Ebrahimi 			continue;
145*8dd5e09dSSadaf Ebrahimi 		len = read(fd, buf, sizeof buf - 1);
146*8dd5e09dSSadaf Ebrahimi 		close(fd);
147*8dd5e09dSSadaf Ebrahimi 		if (len < 40)
148*8dd5e09dSSadaf Ebrahimi 			continue;
149*8dd5e09dSSadaf Ebrahimi 		buf[len] = 0;
150*8dd5e09dSSadaf Ebrahimi 		tmp = strrchr(buf, ')');
151*8dd5e09dSSadaf Ebrahimi 		if (tmp)
152*8dd5e09dSSadaf Ebrahimi 			*tmp = 0;
153*8dd5e09dSSadaf Ebrahimi 		else
154*8dd5e09dSSadaf Ebrahimi 			continue;
155*8dd5e09dSSadaf Ebrahimi 		memset(cmd, 0, sizeof(cmd));
156*8dd5e09dSSadaf Ebrahimi 		sscanf(buf, "%d (%15c", &ppid, cmd);
157*8dd5e09dSSadaf Ebrahimi 		sscanf(tmp+2, "%c %d", &state, &ppid);
158*8dd5e09dSSadaf Ebrahimi 
159*8dd5e09dSSadaf Ebrahimi 		// Skip kthreads
160*8dd5e09dSSadaf Ebrahimi 		if (pid == 2 || ppid == 2)
161*8dd5e09dSSadaf Ebrahimi 			continue;
162*8dd5e09dSSadaf Ebrahimi 
163*8dd5e09dSSadaf Ebrahimi 		if (!show_all && pid == 1)
164*8dd5e09dSSadaf Ebrahimi 			continue;
165*8dd5e09dSSadaf Ebrahimi 
166*8dd5e09dSSadaf Ebrahimi 		// now get the capabilities
167*8dd5e09dSSadaf Ebrahimi 		capng_clear(CAPNG_SELECT_ALL);
168*8dd5e09dSSadaf Ebrahimi 		capng_setpid(pid);
169*8dd5e09dSSadaf Ebrahimi 		if (capng_get_caps_process())
170*8dd5e09dSSadaf Ebrahimi 			continue;
171*8dd5e09dSSadaf Ebrahimi 
172*8dd5e09dSSadaf Ebrahimi 		// And print out anything with capabilities
173*8dd5e09dSSadaf Ebrahimi 		caps = capng_have_capabilities(CAPNG_SELECT_CAPS);
174*8dd5e09dSSadaf Ebrahimi 		if (caps > CAPNG_NONE) {
175*8dd5e09dSSadaf Ebrahimi 			// Get the effective uid
176*8dd5e09dSSadaf Ebrahimi 			FILE *f;
177*8dd5e09dSSadaf Ebrahimi 			int line;
178*8dd5e09dSSadaf Ebrahimi 			snprintf(buf, 32, "/proc/%d/status", pid);
179*8dd5e09dSSadaf Ebrahimi 			f = fopen(buf, "rte");
180*8dd5e09dSSadaf Ebrahimi 			if (f == NULL)
181*8dd5e09dSSadaf Ebrahimi 				euid = 0;
182*8dd5e09dSSadaf Ebrahimi 			else {
183*8dd5e09dSSadaf Ebrahimi 				line = 0;
184*8dd5e09dSSadaf Ebrahimi 				__fsetlocking(f, FSETLOCKING_BYCALLER);
185*8dd5e09dSSadaf Ebrahimi 				while (fgets(buf, sizeof(buf), f)) {
186*8dd5e09dSSadaf Ebrahimi 					if (line == 0) {
187*8dd5e09dSSadaf Ebrahimi 						line++;
188*8dd5e09dSSadaf Ebrahimi 						continue;
189*8dd5e09dSSadaf Ebrahimi 					}
190*8dd5e09dSSadaf Ebrahimi 					if (memcmp(buf, "Uid:", 4) == 0) {
191*8dd5e09dSSadaf Ebrahimi 						int id;
192*8dd5e09dSSadaf Ebrahimi 						sscanf(buf, "Uid: %d %d",
193*8dd5e09dSSadaf Ebrahimi 							&id, &euid);
194*8dd5e09dSSadaf Ebrahimi 						break;
195*8dd5e09dSSadaf Ebrahimi 					}
196*8dd5e09dSSadaf Ebrahimi 				}
197*8dd5e09dSSadaf Ebrahimi 				fclose(f);
198*8dd5e09dSSadaf Ebrahimi 			}
199*8dd5e09dSSadaf Ebrahimi 
200*8dd5e09dSSadaf Ebrahimi 			if (header == 0) {
201*8dd5e09dSSadaf Ebrahimi 				printf("%-5s %-5s %-10s  %-18s  %s\n",
202*8dd5e09dSSadaf Ebrahimi 				    "ppid", "pid", "uid", "command",
203*8dd5e09dSSadaf Ebrahimi 				    "capabilities");
204*8dd5e09dSSadaf Ebrahimi 				header = 1;
205*8dd5e09dSSadaf Ebrahimi 			}
206*8dd5e09dSSadaf Ebrahimi 			if (euid == 0) {
207*8dd5e09dSSadaf Ebrahimi 				// Take short cut for this one
208*8dd5e09dSSadaf Ebrahimi 				name = "root";
209*8dd5e09dSSadaf Ebrahimi 				uid = 0;
210*8dd5e09dSSadaf Ebrahimi 			} else if (euid != uid) {
211*8dd5e09dSSadaf Ebrahimi 				// Only look up if name changed
212*8dd5e09dSSadaf Ebrahimi 				p = getpwuid(euid);
213*8dd5e09dSSadaf Ebrahimi 				uid = euid;
214*8dd5e09dSSadaf Ebrahimi 				if (p)
215*8dd5e09dSSadaf Ebrahimi 					name = p->pw_name;
216*8dd5e09dSSadaf Ebrahimi 				// If not taking this branch, use last val
217*8dd5e09dSSadaf Ebrahimi 			}
218*8dd5e09dSSadaf Ebrahimi 
219*8dd5e09dSSadaf Ebrahimi 			if (in_child_userns(pid))
220*8dd5e09dSSadaf Ebrahimi 				strcat(cmd, " *");
221*8dd5e09dSSadaf Ebrahimi 
222*8dd5e09dSSadaf Ebrahimi 			if (name) {
223*8dd5e09dSSadaf Ebrahimi 				printf("%-5d %-5d %-10s  %-18s  ", ppid, pid,
224*8dd5e09dSSadaf Ebrahimi 					name, cmd);
225*8dd5e09dSSadaf Ebrahimi 			} else
226*8dd5e09dSSadaf Ebrahimi 				printf("%-5d %-5d %-10d  %-18s  ", ppid, pid,
227*8dd5e09dSSadaf Ebrahimi 					uid, cmd);
228*8dd5e09dSSadaf Ebrahimi 			if (caps == CAPNG_PARTIAL) {
229*8dd5e09dSSadaf Ebrahimi 				capng_print_caps_text(CAPNG_PRINT_STDOUT,
230*8dd5e09dSSadaf Ebrahimi 							CAPNG_PERMITTED);
231*8dd5e09dSSadaf Ebrahimi 				if (capng_have_capabilities(
232*8dd5e09dSSadaf Ebrahimi 					    CAPNG_SELECT_AMBIENT) > CAPNG_NONE)
233*8dd5e09dSSadaf Ebrahimi 					printf(" @");
234*8dd5e09dSSadaf Ebrahimi 				if (capng_have_capabilities(CAPNG_SELECT_BOUNDS)
235*8dd5e09dSSadaf Ebrahimi 							 > CAPNG_NONE)
236*8dd5e09dSSadaf Ebrahimi 					printf(" +");
237*8dd5e09dSSadaf Ebrahimi 				printf("\n");
238*8dd5e09dSSadaf Ebrahimi 			} else {
239*8dd5e09dSSadaf Ebrahimi 				printf("full");
240*8dd5e09dSSadaf Ebrahimi 				if (capng_have_capabilities(
241*8dd5e09dSSadaf Ebrahimi 					    CAPNG_SELECT_AMBIENT) > CAPNG_NONE)
242*8dd5e09dSSadaf Ebrahimi 					printf(" @");
243*8dd5e09dSSadaf Ebrahimi 				if (capng_have_capabilities(CAPNG_SELECT_BOUNDS)
244*8dd5e09dSSadaf Ebrahimi 							 > CAPNG_NONE)
245*8dd5e09dSSadaf Ebrahimi 					printf(" +");
246*8dd5e09dSSadaf Ebrahimi 				printf("\n");
247*8dd5e09dSSadaf Ebrahimi 			}
248*8dd5e09dSSadaf Ebrahimi 		}
249*8dd5e09dSSadaf Ebrahimi 	}
250*8dd5e09dSSadaf Ebrahimi 	closedir(d);
251*8dd5e09dSSadaf Ebrahimi 	return 0;
252*8dd5e09dSSadaf Ebrahimi }
253*8dd5e09dSSadaf Ebrahimi 
254*8dd5e09dSSadaf Ebrahimi 
255