diff options
Diffstat (limited to 'libcap-ng-0.7/utils/netcap.c')
-rw-r--r-- | libcap-ng-0.7/utils/netcap.c | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/libcap-ng-0.7/utils/netcap.c b/libcap-ng-0.7/utils/netcap.c new file mode 100644 index 0000000..6eb978d --- /dev/null +++ b/libcap-ng-0.7/utils/netcap.c @@ -0,0 +1,447 @@ +/* + * netcap.c - A program that lists network apps with capabilities + * Copyright (c) 2009-10 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This software may be freely redistributed and/or modified under the + * terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: + * Steve Grubb <sgrubb@redhat.com> + * + * The /proc/net/tcp|udp|raw parsing code was borrowed from netstat.c + */ + +#include "config.h" +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <dirent.h> +#include <fcntl.h> +#include <pwd.h> +#include "cap-ng.h" +#include "proc-llist.h" + +static llist l; +static int perm_warn = 0, header = 0, last_uid = -1; +static char *tacct = NULL; + +static void usage(void) +{ + fprintf(stderr, "usage: netcap\n"); + exit(1); +} + +static int collect_process_info(void) +{ + DIR *d, *f; + struct dirent *ent; + d = opendir("/proc"); + if (d == NULL) { + fprintf(stderr, "Can't open /proc: %s\n", strerror(errno)); + return 1; + } + while (( ent = readdir(d) )) { + FILE *sf; + int pid, ppid; + capng_results_t caps; + char buf[100]; + char *tmp, cmd[16], state, *text, *bounds; + int fd, len, euid = -1; + + // Skip non-process dir entries + if(*ent->d_name<'0' || *ent->d_name>'9') + continue; + errno = 0; + pid = strtol(ent->d_name, NULL, 10); + if (errno) + continue; + + // Parse up the stat file for the proc + snprintf(buf, 32, "/proc/%d/stat", pid); + fd = open(buf, O_RDONLY|O_CLOEXEC, 0); + if (fd < 0) + continue; + len = read(fd, buf, sizeof buf - 1); + close(fd); + if (len < 40) + continue; + buf[len] = 0; + tmp = strrchr(buf, ')'); + if (tmp) + *tmp = 0; + else + continue; + memset(cmd, 0, sizeof(cmd)); + sscanf(buf, "%d (%15c", &ppid, cmd); + sscanf(tmp+2, "%c %d", &state, &ppid); + + // Skip kthreads + if (pid == 2 || ppid == 2) + continue; + + // now get the capabilities + capng_clear(CAPNG_SELECT_BOTH); + capng_setpid(pid); + if (capng_get_caps_process()) + continue; + caps = capng_have_capabilities(CAPNG_SELECT_CAPS); + if (caps <= CAPNG_NONE) + continue; + if (caps == CAPNG_FULL) + text = strdup("full"); + else + text = capng_print_caps_text(CAPNG_PRINT_BUFFER, + CAPNG_PERMITTED); + + // Get the effective uid + snprintf(buf, 32, "/proc/%d/status", pid); + sf = fopen(buf, "rte"); + if (sf == NULL) + euid = 0; + else { + int line = 0; + __fsetlocking(sf, FSETLOCKING_BYCALLER); + while (fgets(buf, sizeof(buf), sf)) { + if (line == 0) { + line++; + continue; + } + if (memcmp(buf, "Uid:", 4) == 0) { + int id; + sscanf(buf, "Uid: %d %d", + &id, &euid); + break; + } + } + fclose(sf); + } + + // Now record the bounding set information + if (caps == CAPNG_PARTIAL) { + caps = capng_have_capabilities(CAPNG_SELECT_BOUNDS); + if (caps == CAPNG_FULL) + bounds = strdup("+"); + else + bounds = strdup(""); + } else + bounds = strdup(""); + + // Now lets get the inodes each process has open + snprintf(buf, 32, "/proc/%d/fd", pid); + f = opendir(buf); + if (f == NULL) { + if (errno == EACCES) { + if (perm_warn == 0) { + printf("You may need to be root to " + "get a full report\n"); + perm_warn = 1; + } + } else + fprintf(stderr, "Can't open %s: %s\n", buf, + strerror(errno)); + free(text); + free(bounds); + continue; + } + // For each file in the fd dir... + while (( ent = readdir(f) )) { + char line[256], ln[256], *s, *e; + unsigned long inode; + lnode node; + int llen; + + if (ent->d_name[0] == '.') + continue; + snprintf(ln, 256, "%s/%s", buf, ent->d_name); + if ((llen = readlink(ln, line, sizeof(line)-1)) < 0) + continue; + line[llen] = 0; + + // Only look at the socket entries + if (memcmp(line, "socket:", 7) == 0) { + // Type 1 sockets + s = strchr(line+7, '['); + if (s == NULL) + continue; + s++; + e = strchr(s, ']'); + if (e == NULL) + continue; + *e = 0; + } else if (memcmp(line, "[0000]:", 7) == 0) { + // Type 2 sockets + s = line + 8; + } else + continue; + errno = 0; + inode = strtoul(s, NULL, 10); + if (errno) + continue; + node.ppid = ppid; + node.pid = pid; + node.uid = euid; + node.cmd = strdup(cmd); + node.inode = inode; + node.capabilities = strdup(text); + node.bounds = strdup(bounds); + // We make one entry for each socket inode + list_append(&l, &node); + } + closedir(f); + free(text); + free(bounds); + } + closedir(d); + return 0; +} + +static void report_finding(int port, const char *type, const char *ifc) +{ + struct passwd *p; + lnode *n = list_get_cur(&l); + + // And print out anything with capabilities + if (header == 0) { + printf("%-5s %-5s %-10s %-16s %-4s %-6s %s\n", + "ppid", "pid", "acct", "command", "type", "port", + "capabilities"); + header = 1; + } + if (n->uid == 0) { + // Take short cut for this one + tacct = "root"; + last_uid = 0; + } else if (last_uid != (int)n->uid) { + // Only look up if name changed + p = getpwuid(n->uid); + last_uid = n->uid; + if (p) + tacct = p->pw_name; + // If not taking this branch, use last val + } + if (tacct) { + printf("%-5d %-5d %-10s", n->ppid, n->pid, tacct); + } else + printf("%-5d %-5d %-10d", n->ppid, n->pid, last_uid); + printf(" %-16s %-4s", n->cmd, type); + if (ifc) + printf(" %-6s", ifc); + else + printf(" %-6d", port); + printf(" %s %s\n", n->capabilities, n->bounds); +} + +static void read_tcp(const char *proc, const char *type) +{ + int line = 0; + FILE *f; + char buf[256]; + unsigned long rxq, txq, time_len, retr, inode; + int local_port, rem_port, d, state, timer_run, uid, timeout; + char rem_addr[128], local_addr[128], more[512]; + + f = fopen(proc, "rte"); + if (f == NULL) { + if (errno != ENOENT) + fprintf(stderr, "Can't open %s: %s\n", + proc, strerror(errno)); + return; + } + __fsetlocking(f, FSETLOCKING_BYCALLER); + while (fgets(buf, sizeof(buf), f)) { + if (line == 0) { + line++; + continue; + } + more[0] = 0; + sscanf(buf, "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X " + "%lX:%lX %X:%lX %lX %d %d %lu %512s\n", + &d, local_addr, &local_port, rem_addr, &rem_port, + &state, &txq, &rxq, &timer_run, &time_len, &retr, + &uid, &timeout, &inode, more); + if (list_find_inode(&l, inode)) + report_finding(local_port, type, NULL); + } + fclose(f); +} + +static void read_udp(const char *proc, const char *type) +{ + int line = 0; + FILE *f; + char buf[256]; + unsigned long rxq, txq, time_len, retr, inode; + int local_port, rem_port, d, state, timer_run, uid, timeout; + char rem_addr[128], local_addr[128], more[512]; + + f = fopen(proc, "rte"); + if (f == NULL) { + if (errno != ENOENT) + fprintf(stderr, "Can't open %s: %s\n", + proc, strerror(errno)); + return; + } + __fsetlocking(f, FSETLOCKING_BYCALLER); + while (fgets(buf, sizeof(buf), f)) { + if (line == 0) { + line++; + continue; + } + more[0] = 0; + sscanf(buf, "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X " + "%lX:%lX %X:%lX %lX %d %d %lu %512s\n", + &d, local_addr, &local_port, rem_addr, &rem_port, + &state, &txq, &rxq, &timer_run, &time_len, &retr, + &uid, &timeout, &inode, more); + if (list_find_inode(&l, inode)) + report_finding(local_port, type, NULL); + } + fclose(f); +} + +static void read_raw(const char *proc, const char *type) +{ + int line = 0; + FILE *f; + char buf[256]; + unsigned long rxq, txq, time_len, retr, inode; + int local_port, rem_port, d, state, timer_run, uid, timeout; + char rem_addr[128], local_addr[128], more[512]; + + f = fopen(proc, "rte"); + if (f == NULL) { + if (errno != ENOENT) + fprintf(stderr, "Can't open %s: %s\n", + proc, strerror(errno)); + return; + } + __fsetlocking(f, FSETLOCKING_BYCALLER); + while (fgets(buf, sizeof(buf), f)) { + if (line == 0) { + line++; + continue; + } + more[0] = 0; + sscanf(buf, "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X " + "%lX:%lX %X:%lX %lX %d %d %lu %512s\n", + &d, local_addr, &local_port, rem_addr, &rem_port, + &state, &txq, &rxq, &timer_run, &time_len, &retr, + &uid, &timeout, &inode, more); + if (list_find_inode(&l, inode)) + report_finding(0, type, NULL); + } + fclose(f); +} + +// Caller must have buffer > 16 bytes +static void get_interface(unsigned int iface, char *ifc) +{ + unsigned int line = 0; + FILE *f; + char buf[256], more[256]; + + // Terminate the interface in case of error + *ifc = 0; + + // Increment the interface number since header is 2 lines long + iface++; + + f = fopen("/proc/net/dev", "rte"); + if (f == NULL) { + if (errno != ENOENT) + fprintf(stderr, "Can't open /proc/net/dev: %s\n", + strerror(errno)); + return; + } + __fsetlocking(f, FSETLOCKING_BYCALLER); + while (fgets(buf, sizeof(buf), f)) { + if (line == iface) { + char *c; + sscanf(buf, "%16s: %256s\n", ifc, more); + c = strchr(ifc, ':'); + if (c) + *c = 0; + fclose(f); + return; + } + line++; + } + fclose(f); +} + +static void read_packet(void) +{ + int line = 0; + FILE *f; + char buf[256]; + unsigned long sk, inode; + unsigned int ref_cnt, type, proto, iface, r, rmem, uid; + char more[256], ifc[32]; + + f = fopen("/proc/net/packet", "rte"); + if (f == NULL) { + if (errno != ENOENT) + fprintf(stderr, "Can't open /proc/net/packet: %s\n", + strerror(errno)); + return; + } + __fsetlocking(f, FSETLOCKING_BYCALLER); + while (fgets(buf, sizeof(buf), f)) { + if (line == 0) { + line++; + continue; + } + more[0] = 0; + sscanf(buf, "%lX %u %u %X %u %u %u %u %lu %256s\n", + &sk, &ref_cnt, &type, &proto, &iface, + &r, &rmem, &uid, &inode, more); + get_interface(iface, ifc); + if (list_find_inode(&l, inode)) + report_finding(0, "pkt", ifc); + } + fclose(f); +} + +int main(int argc, char __attribute__((unused)) *argv[]) +{ + if (argc > 1) { + fputs("Too many arguments\n", stderr); + usage(); + } + + list_create(&l); + collect_process_info(); + + // Now we check the tcp socket list... + read_tcp("/proc/net/tcp", "tcp"); + read_tcp("/proc/net/tcp6", "tcp6"); + + // Next udp sockets... + read_udp("/proc/net/udp", "udp"); + read_udp("/proc/net/udp6", "udp6"); + + // Next, raw sockets... + read_raw("/proc/net/raw", "raw"); + read_raw("/proc/net/raw6", "raw6"); + + // And last, read packet sockets + read_packet(); + + list_clear(&l); + return 0; +} + |