The following two source files comprise the Clutch codebase. To preserve readability, we richly comment the code—but no book-text appears inside the code. You can download the full source files from this book's companion Web site at http://www.wiley.com/compbooks/schiffman.
/* * $Id: clutch.h,v 1.3 2002/05/05 19:30:20 route Exp $ * * Building Open Source Network Security Tools * clutch.h - libdnet example code * * Copyright (c) 2002 Mike D. Schiffman <mike@infonexus.com> * All rights reserved. * */ #include <sys/types.h> #include <ctype.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <dnet.h> /* mode types */ #define ARP 0x1 #define ROUTE 0x2 /* control flags */ #define VERBOSE 0x1 #define ENFORCE 0x2 /* simple macros for code clean up */ #define STEPOVER_WS (b) while (!isgraph(*b)) { b++; } #define STEPOVER_NONWS(b) while (isgraph(*b)) { b++; } struct clutch_pack { u_char flags; /* control flags */ arp_t *a; /* arp cache handle */ route_t *r; /* route table handle */ struct clutch_arp_entry *cae;/* linked list of arp cache entries */ struct clutch_route_entry *cre;/* linked list of route table entries */ }; struct clutch_arp_entry { struct addr mac; /* ethernet address */ struct addr ip; /* ip address */ struct clutch_arp_entry *next; /* next entry in list */ }; struct clutch_route_entry { struct addr ip; /* ip address */ struct addr gw; /* gateway */ struct clutch_route_entry *next;/* next entry in list */ }; int init_clutch(struct clutch_pack *, char *); int parse_config(struct clutch_pack *, FILE *); int new_list_entry(struct clutch_pack **, int, struct addr *, struct addr *); char *get_time(); void free_cp(struct clutch_pack *); int check_arp_cache(const struct arp_entry *, void *); int check_route_table(const struct route_entry *, void *); void usage(char *); /* EOF */
/* * $Id: clutch.c,v 1.3 2002/05/05 19:30:20 route Exp $ * * Building Open Source Network Security Tools * clutch.c - libdnet example code * * Copyright (c) 2002 Mike D. Schiffman <mike@infonexus.com> * All rights reserved. * */ #include "./clutch.h" int main(int argc, char **argv) { int c, n, sleep_int; char *filename; struct clutch_pack cp; printf("Clutch 1.0 [ARP cache / route table monitoring tool]\n"); printf("<ctrl-c> to quit\n"); sleep_int = 1; filename = NULL; while ((c = getopt(argc, argv, "c:ehs:v")) != EOF) { switch (c) { case 'c': filename = optarg; break; case 'e': cp.flags |= ENFORCE; break; case 'h': usage(argv[0]); exit(EXIT_FAILURE); case 'v': cp.flags |= VERBOSE; break; case 's': sleep_int = atoi(optarg); break; default: usage(argv[0]); exit(EXIT_FAILURE); } } if (filename == NULL) { usage(argv[0]); exit(EXIT_FAILURE); } if (cp.flags & VERBOSE) { printf("Verbose mode is on.\n"); } if (cp.flags & ENFORCE) { printf("Strict policy enforcement in effect.\n"); } /* * Initialize the program. Open all file handles and parse the * configuration file. */ n = init_clutch(&cp, filename); if (n == -1) { return (EXIT_FAILURE); } if (n == 0) { fprintf(stderr, "No rules to process!\n"); return (EXIT_FAILURE); } fprintf(stderr, "State database loaded (%d rule(s)).\n", n); fprintf(stderr, "Program initialized, watching for violations...\n"); for (; ; sleep(sleep_int)) { /* * Run through the ARP cache and routing table and check them * against our rules to ensure no malcontents have tampered * with them. * * One thing to notice about this program is that we don't * explicitly free memory anywhere. This isn't considered a * high priority however, since once we malloc memory for our * state database, we need all of it until the program quits, * in which case we rely on the operating system to reclaim * our used resources. Besides, we're done at that point, so * who cares! */ if (arp_loop(cp.a, check_arp_cache, &cp) == -1) { fprintf(stderr, "error checking ARP cache\n"); } if (route_loop(cp.r, check_route_table, &cp) == -1) { fprintf(stderr, "error checking route table\n"); } } exit(EXIT_SUCCESS); } int init_clutch(struct clutch_pack *cp, char *filename) { int n; FILE *fp; /* open the config file passed in the by user at the CLI */ fp = fopen(filename, "r+"); if (fp == NULL) { perror("init_clutch: fopen"); return (-1); } /* get an ARP cache handle */ cp->a = arp_open(); if (cp->a == NULL) { perror("init_clutch: arp_open"); goto bad; } /* get a route table handle */ cp->r = route_open(); if (cp->r == NULL) { perror("init_clutch: route_open"); goto bad; } /* * Parse the configuration file and build the state table for * Clutch. */ n = parse_config(cp, fp); if (n == -1) { fprintf(stderr, "parse_config fatal error\n"); goto bad; } else { return (n); } bad: arp_close(cp-> a); route_close (cp-> r); return (-1); } int parse_config(struct clutch_pack *cp, FILE *fp) { int l, m; char buf[BUFSIZ]; char *mac_p, *ip_p, *gw_p, *end_p; struct addr ip; struct addr gw; struct addr mac; /* * Parse the config file with the following logic: * * - Ignore all lines beginning with "#" or a whitespace character * - If a line starts with ARP, parse it as an ARP mapping: * - Expect "x:x:x:x:x:x -> y.y.y.y" * - Non-fatal continue error if there's a lexical problem * - Otherwise store it in the ARP cache mapping list * - If a line starts with INT, parse it as an interface entry: * - Expect "device flags" * - Non-fatal continue error if there's a lexical problem * - Otherwise store it in the interface list * - If a line starts with RTE, parse it as a route entry: * - Expect "x.x.x.x -> y.y.y.y" * - Non-fatal continue error if there's a lexical problem * - Otherwise store it in the route table list * - Everything else is a non-fatal error */ l = 0; m = 0; while (fgets(buf, sizeof (buf) - 1, fp)) { /* count configuration file lines */ 1++; if (isspace(buf[0]) || buf[0] == '#') { /* blank link or comment */ continue; } if (strstr(buf, "ARP")) { mac_p = buf; ip_p = strstr(buf, "-> "); if (ip_p == NULL) { goto error; } /* step past "ARP" */ mac_p += 3; /* step past "->" */ ip_p += 2; /* remove whitespace */ STEPOVER_WS(mac_p); end_p = mac_p; /* get to the end of the MAC */ while (isgraph(*end_p) && !(*end_p == '-')) { end_p++; } *end_p = NULL; if (addr_aton(mac_p, &mac) == -1) { goto error; } /* remove whitespace */ STEPOVER_WS(ip_p); end_p = ip_p; /* get to the end of IP */ STEPOVER_NONWS(end_p); *end_p = NULL; if (addr_aton(ip_p, &ip) == -1) { goto error; } /* scrape together some memory for the ARP entry here */ if (new_list_entry(&cp, ARP, &mac, &ip) == -1) { perror("malloc"); return (-1); } m++; if ((cp->flags) & VERBOSE) { printf("added ARP mapping rule %s -> %s\n", addr_ntoa(&mac), addr_ntoa(&ip)); } } else if (strstr(buf, "RTE")) { ip_p = buf; gw_p = strstr(buf, "->"); // find next part of the data gw_p += 2; ip_p += 3; /* remove whitespace */ STEPOVER_WS(ip_p); end_p = ip_p; /* get to the end of IP */ while (isgraph(*end_p) && !(*end_p == '-')) { end_p++; } *end_p = NULL; if (addr_aton(ip_p, &ip) == -1) { goto error; } /* remove whitespace */ STEPOVER_WS(gw_p); end_p = gw_p; STEPOVER_NONWS(end_p); *end_p = NULL; if (addr_aton(gw_p, &gw) == -1) { goto error; } /* scrape together some memory for the route entry here */ if (new_list_entry(&cp, ROUTE, &ip, &gw) == -1) { perror("malloc"); return (-1); } m++; if ((cp-> flags) & VERBOSE) { printf("added route table rule %s -> %s\n", addr_ntoa(&ip), addr_ntoa(&gw)); } } else { error: fprintf(stderr, "unknown or malformed rule at line %03d\n", 1); } } return (m); } int new_list_entry(struct clutch_pack **cp, int type, struct addr *al, struct addr *a2) { switch (type) { case ARP: { struct clutch_arp_entry *p; if ((*cp)->cae == NULL) { /* create the head node on the list */ (*cp)-> cae = malloc(sizeof (struct clutch_arp_entry)); if ((*cp)-> cae == NULL) { return (-1); } memset((*cp)-> cae, 0, sizeof (struct clutch_arp_entry)); memcpy(&(*cp)-> cae->mac, al, sizeof (struct addr)); memcpy(&(*cp)->cae->ip, a2, sizeof (struct addr)); (*cp)-> cae-> next = NULL; return (1); } else { /* walk to the end of the list */ for (p = (*cp)-> cae; p-> next; p = p-> next); p->next = malloc(sizeof (struct clutch_arp_entry)); if (p-> next == NULL) { return (-1); } memset(p->next, 0, sizeof (struct clutch_arp_entry) ); p = p-> next; memcpy(&p->mac, al, sizeof (struct addr)); memcpy(&p->ip, a2, sizeof (struct addr)); p->next = NULL; return (1); } } case ROUTE: { struct clutch_route_entry *p; if ((*cp)-> cre == NULL) { /* create the head node on the list */ (*cp)-> cre = malloc(sizeof (struct clutch_route_entry)); if ((*cp)-> cre == NULL) { return (-1); } memset((*cp)-> cre, 0, sizeof (struct clutch_route_entry)); memcpy(&(*cp)-> cre->ip, a1, sizeof (struct addr)); memcpy(&(*cp)-> cre->gw, a2, sizeof (struct addr)); (*cp)-> cre-> next = NULL; return (1); } else { /* walk to the end of the list */ for (p = (*cp)->cre; p->next; p = p->next); p->next = malloc(sizeof (struct clutch_route_entry)); if (p-> next == NULL) { return (-1); } memset(p->next, 0, sizeof (struct clutch_route_entry)); p = p-> next; memcpy(&p->ip, al, sizeof (struct addr)); memcpy(&p->gw, a2, sizeof (struct addr)); p->next = NULL; } return (1); } default: { return (-1); } } return (-1); } int check_arp_cache(const struct arp_entry *ae, void *cp) { struct clutch_pack *p; struct clutch_arp_entry *cae; const struct addr *pa; const struct addr *ha; p = (struct clutch_pack *)cp; pa = &ae->arp_pa; ha = &ae->arp_ha; /* run through the ARP cache rules */ for (cae = (struct clutch_arp_entry *)p->cae; cae; cae = cae->next) { /* look for a hardware address match in the ARP cache */ if (addr_cmp(ha, &cae->mac) == 0) { /* does it match our rule? */ if (addr_cmp(pa, &cae->ip) ! = 0) { printf("[%s ARP cache rule violation: %s -> %s] \n", get_time(), addr_ntoa(ha), addr_ntoa(pa)); if ( (p->flags) & VERBOSE) { printf("[entry should be: %s -> %s]\n", addr_ntoa(&cae->mac), addr_ntoa(&cae->ip)); } if ((p-> flags) & ENFORCE) { /* reset the entry back to what it should be */ if (arp_delete(p-> a, ae) == -1) { fprintf(stderr, "[can't reset ARP entry]\n"); } else { /* setup new ARP entry */ struct arp_entry new_ae; memcpy (&new_ae.arp_pa, &cae->ip, sizeof (struct addr)); memcpy (&new_ae.arp_ha, &cae->mac, sizeof (struct addr)); printf("[bogus ARP cache entry deleted]\n"); if (arp_add(p->a, &new_ae) == -1) { fprintf(stderr, "[can't reset ARP entry]\n"); } else { printf("[correct ARP cache entry restred]\n"); } } } } } } return (0); } int check_route_table(const struct route_entry *re, void *cp) { struct clutch_pack *p; struct clutch_route_entry *cre; const struct addr *dst; const struct addr *gw; p = (struct clutch_pack *)cp; dst = &re->route_dst; gw = &re->route_gw; /* run through the route table rules */ for (cre = (struct clutch_route_entry *)p->cre; cre; cre = cre->next) { /* look for a destination IP match in the route table */ if (addr_cmp(dst, &cre->ip) == 0) { /* does it match our rule? */ if (addr_cmp(gw, &cre->gw) ! = 0) { printf("[%s route table rule violation: %s ->%s]\n", get_time(), addr_ntoa(dst), addr_ntoa(gw)); if ((p-> flags) & VERBOSE) { printf("[entry should be: %s -> %s]\n", addr_ntoa(&cre->ip), addr_ntoa(&cre->gw)); } if ((p->flags) & ENFORCE) { /* reset the entry back to what it should be */ if (route_delete(p->r, re) == -1) { fprintf(stderr, "[can't reset route entry]\n"); } else { /* setup new route entry */ struct route_entry new_re; memcpy (&new_re.route_dst, &cre->ip, sizeof (struct addr)); memcpy (&new_re.route_gw, &cre->gw, sizeof (struct addr)); printf ("[bogus route table entry deleted]\n"); if (route_add(p->r, &new_re) == -1) { fprintf(stderr, "[can't reset rte entry]\n"); } else { printf( "[correct route table entry restored]\n"); } } } } } return ( 0 ); } char * get_time() { int i ; time_t t; static char buf[26]; t = time((time_t *)NULL); strcpy(buf, ctime(&t)); /* cut out the day, year and \n */ for (i = 0; i < 20; i++) { buf[i]= buf[i + 4]; } buf[15]= 0; return (buf); } void usage(char *name) { fprintf(stderr, "usage: %s [options] -c config_file:\n" "-c filename\tconfiguration file\n" "-e\t\tenforce rules rather than just warn\n" "-h\t\tthis jonk here\n" "-s\t\tsleep interval in seconds\n" "-v\t\tbe more verbose\n", name); } /* EOF */