dwmstatus-mitm.c (3958B)
1 /* Here is a helper function that warns you if someone tries to sniff your 2 * network traffic (i.e. a Man-In-The-Middle attack ran against you thanks 3 * to ARP cache poisoning). 4 * 5 * It must be called regularly because it monitors changes in the ARP table 6 * If a host got a new MAC address, it will alert during ALERT_TIMEOUT seconds. 7 * If the MAC address remains the same, it assumes it is just a new host. 8 * Otherwise, if it keep changing, it will keep on alerting. 9 * 10 * Returns true on success, false otherwise. 11 * 12 * Written by vladz (vladz AT devzero.fr). 13 * Updated by mephesto1337 ( dwm-status AT junk-mail.fr ) 14 */ 15 16 #include <arpa/inet.h> 17 #include <linux/if_ether.h> 18 #include <netinet/in.h> 19 #include <stdbool.h> 20 #include <stdint.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <time.h> 25 26 27 // Some useful macros 28 #define CHK(expr, cond) \ 29 do { \ 30 if ( (expr) cond ) { \ 31 fprintf(stderr, "%s failed", #expr); \ 32 goto fail; \ 33 } \ 34 } while ( 0 ) 35 36 #define CHK_NEG(expr) CHK((long)(expr), < 0L) 37 #define CHK_FALSE(expr) CHK(!!(expr), == false) 38 #define CHK_NULL(expr) CHK(expr, == NULL) 39 #define SAFE_FREE(func, ptr) \ 40 do { \ 41 if ( ptr != NULL ) { \ 42 func(ptr); \ 43 } \ 44 ptr = NULL; \ 45 } while ( 0 ) 46 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 47 48 #define ALERT_TIMEOUT ((time_t)40L) // In seconds 49 50 /* The hard maximum number of entries kept in the ARP cache is obtained via 51 * "sysctl net.ipv4.neigh.default.gc_thresh3" (see arp(7)). Default value 52 * for Linux is 1024. 53 */ 54 #define MAX_ARP_CACHE_ENTRIES 1024 55 56 57 struct ether_ip_s { 58 union { 59 uint8_t mac[ETH_ALEN]; 60 unsigned long lmac; 61 }; 62 time_t last_changed; 63 in_addr_t ip; 64 }; 65 66 struct ether_ip_s table[MAX_ARP_CACHE_ENTRIES]; 67 size_t table_size = 0; 68 69 70 bool lookup_and_insert(const struct ether_ip_s *new); 71 72 bool check_arp_table(char *message, size_t len) { 73 FILE *f; 74 struct ether_ip_s tmp; 75 char ip_address[32]; 76 char mac_address[32]; 77 78 snprintf(message, len, "ARP table OK"); 79 CHK_NULL(f = fopen("/proc/net/arp", "r")); 80 time(&tmp.last_changed); 81 fscanf(f, "%*[^\n]\n"); 82 while ( !feof(f) ) { 83 CHK(fscanf(f, "%s%*[ ]0x%*x%*[ ]0x%*x%*[ ]%[a-f0-9:]%*[^\n]\n", ip_address, mac_address), != 2); 84 CHK_NEG(inet_pton(AF_INET, ip_address, &tmp.ip)); 85 86 tmp.lmac = 0UL; 87 CHK(sscanf( 88 mac_address, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", 89 &tmp.mac[0], &tmp.mac[1], &tmp.mac[2], &tmp.mac[3], &tmp.mac[4], &tmp.mac[5] 90 ), != 6); 91 92 if ( ! lookup_and_insert(&tmp) ) { 93 snprintf(message, len, "Possible MITM attack, please check %s", ip_address); 94 break; 95 } 96 } 97 SAFE_FREE(fclose, f); 98 return true; 99 100 fail: 101 SAFE_FREE(fclose, f); 102 snprintf(message, len, "ARP table ???"); 103 return false; 104 } 105 106 bool lookup_and_insert(const struct ether_ip_s *new) { 107 for ( size_t i = 0; i < table_size; i++ ) { 108 if ( table[i].ip == new->ip ) { 109 if ( table[i].lmac != new->lmac ) { 110 if ( table[i].last_changed + ALERT_TIMEOUT > new->last_changed ) { 111 return false; 112 } else { 113 // Update the DB, it must be a new host 114 table[i].lmac = new->lmac; 115 table[i].last_changed = new->last_changed; 116 return true; 117 } 118 } else { 119 // Update last seen 120 table[i].last_changed = new->last_changed; 121 return true; 122 } 123 } 124 } 125 126 if ( table_size < ARRAY_SIZE(table) ) { 127 memcpy(&table[table_size], new, sizeof(struct ether_ip_s)); 128 table_size++; 129 } else { 130 // To big, let's restart from the begining 131 table_size = 0; 132 return false; 133 } 134 135 return true; 136 }