xref: /aosp_15_r20/external/libcap-ng/utils/netcap.c (revision 8dd5e09d5faf27a871e8654ddaa2d2af7c696578)
1 /*
2  * netcap.c - A program that lists network apps with capabilities
3  * Copyright (c) 2009-10,2012,2020 Red Hat Inc.
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., 51 Franklin Street, Fifth Floor
19  * Boston, MA 02110-1335, USA.
20  *
21  * Authors:
22  *   Steve Grubb <[email protected]>
23  *
24  * The /proc/net/tcp|udp|raw parsing code was borrowed from netstat.c
25  */
26 
27 #include "config.h"
28 #include <stdio.h>
29 #include <stdio_ext.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 #include "proc-llist.h"
38 
39 static llist l;
40 static int perm_warn = 0, header = 0, last_uid = -1;
41 static char *tacct = NULL;
42 
usage(void)43 static void usage(void)
44 {
45 	fprintf(stderr, "usage: netcap\n");
46 	exit(1);
47 }
48 
collect_process_info(void)49 static int collect_process_info(void)
50 {
51 	DIR *d, *f;
52 	struct dirent *ent;
53 	d = opendir("/proc");
54 	if (d == NULL) {
55 		fprintf(stderr, "Can't open /proc: %s\n", strerror(errno));
56 		return 1;
57 	}
58 	while (( ent = readdir(d) )) {
59 		FILE *sf;
60 		int pid, ppid;
61 		capng_results_t caps;
62 		char buf[100];
63 		char *tmp, cmd[16], state;
64 		char *text = NULL, *bounds = NULL, *ambient = NULL;
65 		int fd, len, euid = -1;
66 
67 		// Skip non-process dir entries
68 		if(*ent->d_name<'0' || *ent->d_name>'9')
69 			continue;
70 		errno = 0;
71 		pid = strtol(ent->d_name, NULL, 10);
72 		if (errno)
73 			continue;
74 
75 		// Parse up the stat file for the proc
76 		snprintf(buf, 32, "/proc/%d/stat", pid);
77 		fd = open(buf, O_RDONLY|O_CLOEXEC, 0);
78 		if (fd < 0)
79 			continue;
80 		len = read(fd, buf, sizeof buf - 1);
81 		close(fd);
82 		if (len < 40)
83 			continue;
84 		buf[len] = 0;
85 		tmp = strrchr(buf, ')');
86 		if (tmp)
87 			*tmp = 0;
88 		else
89 			continue;
90 		memset(cmd, 0, sizeof(cmd));
91 		sscanf(buf, "%d (%15c", &ppid, cmd);
92 		sscanf(tmp+2, "%c %d", &state, &ppid);
93 
94 		// Skip kthreads
95 		if (pid == 2 || ppid == 2)
96 			continue;
97 
98 		// now get the capabilities
99 		capng_clear(CAPNG_SELECT_ALL);
100 		capng_setpid(pid);
101 		if (capng_get_caps_process())
102 			continue;
103 		caps = capng_have_capabilities(CAPNG_SELECT_CAPS);
104 		if (caps <= CAPNG_NONE)
105 			continue;
106 		if (caps == CAPNG_FULL) {
107 			text = strdup("full");
108 			if (!text) {
109 				fprintf(stderr, "Out of memory\n");
110 				continue;
111 			}
112 		} else {
113 			text = capng_print_caps_text(CAPNG_PRINT_BUFFER,
114 					CAPNG_PERMITTED);
115 			if (text == NULL) {
116 				fprintf(stderr, "Out of memory doing pid %d\n",
117 					pid);
118 				continue;
119 			}
120 		}
121 
122 		// Get the effective uid
123 		snprintf(buf, 32, "/proc/%d/status", pid);
124 		sf = fopen(buf, "rte");
125 		if (sf == NULL)
126 			euid = 0;
127 		else {
128 			int line = 0;
129 			__fsetlocking(sf, FSETLOCKING_BYCALLER);
130 			while (fgets(buf, sizeof(buf), sf)) {
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(sf);
143 		}
144 
145 		caps = capng_have_capabilities(CAPNG_SELECT_AMBIENT);
146 		if (caps > CAPNG_NONE)
147 			ambient = strdup("@");
148 		else
149 			ambient = strdup("");
150 		if (!ambient) {
151 			fprintf(stderr, "Out of memory\n");
152 			free(text);
153 			continue;
154 		}
155 
156 		// Now record the bounding set information
157 		caps = capng_have_capabilities(CAPNG_SELECT_BOUNDS);
158 		if (caps > CAPNG_NONE)
159 			bounds = strdup("+");
160 		else
161 			bounds = strdup("");
162 		if (!bounds) {
163 			fprintf(stderr, "Out of memory\n");
164 			free(text);
165 			free(ambient);
166 			continue;
167 		}
168 
169 		// Now lets get the inodes each process has open
170 		snprintf(buf, 32, "/proc/%d/fd", pid);
171 		f = opendir(buf);
172 		if (f == NULL) {
173 			if (errno == EACCES) {
174 				if (perm_warn == 0) {
175 					fprintf(stderr,
176 						"You may need to be root to "
177 						"get a full report\n");
178 					perm_warn = 1;
179 				}
180 			} else
181 				fprintf(stderr, "Can't open %s: %s\n", buf,
182 					strerror(errno));
183 			free(text);
184 			free(bounds);
185 			free(ambient);
186 			continue;
187 		}
188 		// For each file in the fd dir...
189 		while (( ent = readdir(f) )) {
190 			char line[256], ln[256], *s, *e;
191 			unsigned long inode;
192 			lnode node;
193 			int llen;
194 
195 			if (ent->d_name[0] == '.')
196 				continue;
197 			snprintf(ln, 256, "%s/%s", buf, ent->d_name);
198 			if ((llen = readlink(ln, line, sizeof(line)-1)) < 0)
199 				continue;
200 			line[llen] = 0;
201 
202 			// Only look at the socket entries
203 			if (memcmp(line, "socket:", 7) == 0) {
204 				// Type 1 sockets
205 				s = strchr(line+7, '[');
206 				if (s == NULL)
207 					continue;
208 				s++;
209 				e = strchr(s, ']');
210 				if (e == NULL)
211 					continue;
212 				*e = 0;
213 			} else if (memcmp(line, "[0000]:", 7) == 0) {
214 				// Type 2 sockets
215 				s = line + 8;
216 			} else
217 				continue;
218 			errno = 0;
219 			inode = strtoul(s, NULL, 10);
220 			if (errno)
221 				continue;
222 			node.ppid = ppid;
223 			node.pid = pid;
224 			node.uid = euid;
225 			node.cmd = strdup(cmd);
226 			node.inode = inode;
227 			node.capabilities = strdup(text);
228 			node.bounds = strdup(bounds);
229 			node.ambient = strdup(ambient);
230 			if (node.cmd && node.capabilities && node.bounds &&
231 			    node.ambient)
232 				// We make one entry for each socket inode
233 				list_append(&l, &node);
234 			else {
235 				free(node.cmd);
236 				free(node.capabilities);
237 				free(node.bounds);
238 				free(node.ambient);
239 			}
240 		}
241 		closedir(f);
242 		free(text);
243 		free(bounds);
244 		free(ambient);
245 	}
246 	closedir(d);
247 	return 0;
248 }
249 
report_finding(unsigned int port,const char * type,const char * ifc)250 static void report_finding(unsigned int port, const char *type, const char *ifc)
251 {
252 	struct passwd *p;
253 	lnode *n = list_get_cur(&l);
254 
255 	// And print out anything with capabilities
256 	if (header == 0) {
257 		printf("%-5s %-5s %-10s %-16s %-8s %-6s %s\n",
258 			"ppid", "pid", "acct", "command", "type", "port",
259 			"capabilities");
260 			header = 1;
261 	}
262 	if (n->uid == 0) {
263 		// Take short cut for this one
264 		tacct = "root";
265 		last_uid = 0;
266 	} else if (last_uid != (int)n->uid) {
267 		// Only look up if name changed
268 		p = getpwuid(n->uid);
269 		last_uid = n->uid;
270 		if (p)
271 			tacct = p->pw_name;
272 		// If not taking this branch, use last val
273 	}
274 	if (tacct) {
275 		printf("%-5d %-5d %-10s", n->ppid, n->pid, tacct);
276 	} else
277 		printf("%-5d %-5d %-10d", n->ppid, n->pid, last_uid);
278 	printf(" %-16s %-8s", n->cmd, type);
279 	if (ifc)
280 		printf(" %-6s", ifc);
281 	else
282 		printf(" %-6u", port);
283 	printf(" %s %s%s\n", n->capabilities, n->ambient, n->bounds);
284 }
285 
read_tcp(const char * proc,const char * type)286 static void read_tcp(const char *proc, const char *type)
287 {
288 	int line = 0;
289 	FILE *f;
290 	char buf[256];
291 	unsigned long rxq, txq, time_len, retr, inode;
292 	unsigned int local_port, rem_port, state, timer_run;
293 	int d, uid, timeout;
294 	char rem_addr[128], local_addr[128], more[512];
295 
296 	f = fopen(proc, "rte");
297 	if (f == NULL) {
298 		if (errno != ENOENT)
299 			fprintf(stderr, "Can't open %s: %s\n",
300 				proc, strerror(errno));
301 		return;
302 	}
303 	__fsetlocking(f, FSETLOCKING_BYCALLER);
304 	while (fgets(buf, sizeof(buf), f)) {
305 		if (line == 0) {
306 			line++;
307 			continue;
308 		}
309 		more[0] = 0;
310 		sscanf(buf, "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X "
311 			"%lX:%lX %X:%lX %lX %d %d %lu %511s\n",
312 			&d, local_addr, &local_port, rem_addr, &rem_port,
313 			&state, &txq, &rxq, &timer_run, &time_len, &retr,
314 			&uid, &timeout, &inode, more);
315 		if (list_find_inode(&l, inode))
316 			report_finding(local_port, type, NULL);
317 	}
318 	fclose(f);
319 }
320 
read_udp(const char * proc,const char * type)321 static void read_udp(const char *proc, const char *type)
322 {
323 	int line = 0;
324 	FILE *f;
325 	char buf[256];
326 	unsigned long rxq, txq, time_len, retr, inode;
327 	unsigned int local_port, rem_port, state, timer_run;
328 	int d, uid, timeout;
329 	char rem_addr[128], local_addr[128], more[512];
330 
331 	f = fopen(proc, "rte");
332 	if (f == NULL) {
333 		if (errno != ENOENT)
334 			fprintf(stderr, "Can't open %s: %s\n",
335 					proc, strerror(errno));
336 		return;
337 	}
338 	__fsetlocking(f, FSETLOCKING_BYCALLER);
339 	while (fgets(buf, sizeof(buf), f)) {
340 		if (line == 0) {
341 			line++;
342 			continue;
343 		}
344 		more[0] = 0;
345 		sscanf(buf, "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X "
346 			"%lX:%lX %X:%lX %lX %d %d %lu %511s\n",
347 			&d, local_addr, &local_port, rem_addr, &rem_port,
348 			&state, &txq, &rxq, &timer_run, &time_len, &retr,
349 			&uid, &timeout, &inode, more);
350 		if (list_find_inode(&l, inode))
351 			report_finding(local_port, type, NULL);
352 	}
353 	fclose(f);
354 }
355 
read_raw(const char * proc,const char * type)356 static void read_raw(const char *proc, const char *type)
357 {
358 	int line = 0;
359 	FILE *f;
360 	char buf[256];
361 	unsigned long rxq, txq, time_len, retr, inode;
362 	unsigned int local_port, rem_port, state, timer_run;
363 	int d, uid, timeout;
364 	char rem_addr[128], local_addr[128], more[512];
365 
366 	f = fopen(proc, "rte");
367 	if (f == NULL) {
368 		if (errno != ENOENT)
369 			fprintf(stderr, "Can't open %s: %s\n",
370 					proc, strerror(errno));
371 		return;
372 	}
373 	__fsetlocking(f, FSETLOCKING_BYCALLER);
374 	while (fgets(buf, sizeof(buf), f)) {
375 		if (line == 0) {
376 			line++;
377 			continue;
378 		}
379 		more[0] = 0;
380 		sscanf(buf, "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X "
381 			"%lX:%lX %X:%lX %lX %d %d %lu %511s\n",
382 			&d, local_addr, &local_port, rem_addr, &rem_port,
383 			&state, &txq, &rxq, &timer_run, &time_len, &retr,
384 			&uid, &timeout, &inode, more);
385 		if (list_find_inode(&l, inode))
386 			report_finding(0, type, NULL);
387 	}
388 	fclose(f);
389 }
390 
391 // Caller must have buffer > 16 bytes
get_interface(unsigned int iface,char * ifc)392 static void get_interface(unsigned int iface, char *ifc)
393 {
394 	unsigned int line = 0;
395 	FILE *f;
396 	char buf[256], more[256];
397 
398 	// Terminate the interface in case of error
399 	*ifc = 0;
400 
401 	// Increment the interface number since header is 2 lines long
402 	iface++;
403 
404 	f = fopen("/proc/net/dev", "rte");
405 	if (f == NULL) {
406 		if (errno != ENOENT)
407 			fprintf(stderr, "Can't open /proc/net/dev: %s\n",
408 				strerror(errno));
409 		return;
410 	}
411 	__fsetlocking(f, FSETLOCKING_BYCALLER);
412 	while (fgets(buf, sizeof(buf), f)) {
413 		if (line == iface) {
414 			char *c;
415 			sscanf(buf, "%16s: %255s\n", ifc, more);
416 			c = strchr(ifc, ':');
417 			if (c)
418 				*c = 0;
419 			fclose(f);
420 			return;
421 		}
422 		line++;
423 	}
424 	fclose(f);
425 }
426 
read_packet(void)427 static void read_packet(void)
428 {
429 	int line = 0;
430 	FILE *f;
431 	char buf[256];
432 	unsigned long sk, inode;
433 	unsigned int ref_cnt, type, proto, iface, r, rmem, uid;
434 	char more[256], ifc[32];
435 
436 	f = fopen("/proc/net/packet", "rte");
437 	if (f == NULL) {
438 		if (errno != ENOENT)
439 			fprintf(stderr, "Can't open /proc/net/packet: %s\n",
440 				strerror(errno));
441 		return;
442 	}
443 	__fsetlocking(f, FSETLOCKING_BYCALLER);
444 	while (fgets(buf, sizeof(buf), f)) {
445 		if (line == 0) {
446 			line++;
447 			continue;
448 		}
449 		more[0] = 0;
450 		sscanf(buf, "%lX %u %u %X %u %u %u %u %lu %255s\n",
451 			&sk, &ref_cnt, &type, &proto, &iface,
452 			&r, &rmem, &uid, &inode, more);
453 		get_interface(iface, ifc);
454 		if (list_find_inode(&l, inode))
455 			report_finding(0, "pkt", ifc);
456 	}
457 	fclose(f);
458 }
459 
main(int argc,char * argv[])460 int main(int argc, char __attribute__((unused)) *argv[])
461 {
462 	if (argc > 1) {
463 		fputs("Too many arguments\n", stderr);
464 		usage();
465 	}
466 
467 	list_create(&l);
468 	collect_process_info();
469 
470 	// Now we check the tcp socket list...
471 	read_tcp("/proc/net/tcp", "tcp");
472 	read_tcp("/proc/net/tcp6", "tcp6");
473 
474 	// Next udp sockets...
475 	read_udp("/proc/net/udp", "udp");
476 	read_udp("/proc/net/udp6", "udp6");
477 	read_udp("/proc/net/udplite", "udplite");
478 	read_udp("/proc/net/udplite6", "udplite6");
479 
480 	// Next, raw sockets...
481 	read_raw("/proc/net/raw", "raw");
482 	read_raw("/proc/net/raw6", "raw6");
483 
484 	// And last, read packet sockets
485 	read_packet();
486 
487 	// Could also do icmp,netlink,unix
488 
489 	list_clear(&l);
490 	return 0;
491 }
492 
493