sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | 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 }