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 }