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