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