1/* 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 448