dwmstatus-mitm.c - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
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 }