#include "isowall.h" #include "string_s.h" #include "logger.h" #include "rawsock.h" #include "pixie-threads.h" #include #include #if defined(WIN32) #include #else #include #endif /**************************************************************************** * @return * 1 if the packet is OK * 0 if the packet is bad ****************************************************************************/ static int is_valid(const unsigned char *px, size_t length, const struct Adapter *src, const struct Adapter *dst, const struct RangeList *allowed, int is_inbound) { unsigned ethertype; /* * validate: * Ethernet frames are a minimum of 60 bytes. This also makes sure * there is enough to contain the full IP header or ARP packet */ if (length < 42) return 0; /* * validate: * Etheretype must be IPv4 or ARP */ ethertype = px[12]<<8 | px[13]; if (ethertype != 0x800 && ethertype != 0x806) return 0; /* * validate: * MAC address of this packet must be only our target and the * router, going in the correct direction */ if (memcmp(px+0, "\xFF\xFF\xFF\xFF\xFF\xFF", 6) == 0 && ethertype == 0x806) ; /* ARP packets can be broadcast packets instead */ else if (memcmp(px+0, dst->target_mac, 6) != 0) return 0; if (memcmp(px+6, "\x00\x00\x00\x00\x00\x00", 6) == 0) ; /* temporary kludge for VMware testing */ else if (memcmp(px+6, src->target_mac, 6) != 0) return 0; /* * If IPv4, extract addresses and return */ if (ethertype == 0x800) { unsigned src_ip; unsigned dst_ip; src_ip = px[14+12+0]<<24 | px[14+12+1]<<16 | px[14+12+2]<< 8 | px[14+12+3]<< 0; dst_ip = px[14+16+0]<<24 | px[14+16+1]<<16 | px[14+16+2]<< 8 | px[14+16+3]<< 0; if (is_inbound) { if (dst_ip != dst->target_ip) return 0; if (!rangelist_is_contains(allowed, src_ip)) return 0; } else { if (src_ip != src->target_ip) return 0; if (!rangelist_is_contains(allowed, dst_ip)) return 0; } return 1; } else if (ethertype == 0x806) { struct ARP_Packet { unsigned opcode; unsigned hardware_type; unsigned protocol_type; unsigned hardware_length; unsigned protocol_length; unsigned ip_src; unsigned ip_dst; const unsigned char *mac_src; const unsigned char *mac_dst; } arp[1]; unsigned offset; /* * Parse the basic ARP header */ offset = 14; arp->hardware_type = px[offset]<<8 | px[offset+1]; arp->protocol_type = px[offset+2]<<8 | px[offset+3]; arp->hardware_length = px[offset+4]; arp->protocol_length = px[offset+5]; arp->opcode = px[offset+6]<<8 | px[offset+7]; offset += 8; /* We only support IPv4 and Ethernet addresses */ if (arp->protocol_length != 4 && arp->hardware_length != 6) return 0; if (arp->protocol_type != 0x0800) return 0; if (arp->hardware_type != 1 && arp->hardware_type != 6) return 0; /* * parse the addresses */ arp->mac_src = px+offset; offset += arp->hardware_length; arp->ip_src = px[offset+0]<<24 | px[offset+1]<<16 | px[offset+2]<<8 | px[offset+3]; offset += arp->protocol_length; arp->mac_dst = px+offset; offset += arp->hardware_length; arp->ip_dst = px[offset+0]<<24 | px[offset+1]<<16 | px[offset+2]<<8 | px[offset+3]; offset += arp->protocol_length; /* validate: * Validate that source/destination are correct */ if (arp->ip_src != src->target_ip) return 0; if (memcmp(arp->mac_src, src->target_mac, 6) != 0) return 0; if (arp->ip_dst != dst->target_ip) return 0; /* * Validate depending on opcode */ switch (arp->opcode) { case 1: /* request */ /* no further validation needs to be done */ break; case 2: /* reply */ /* validate: * make sure the destination MAC address is correct */ if (memcmp(arp->mac_dst, dst->target_mac, 6) != 0) return 0; /* validate: * make sure this isn't a broadcast packet; we allowed it above * but in this case, it shouldn't be valid */ if (memcmp(px+0, "\xFF\xFF\xFF\xFF\xFF\xFF", 6) == 0) return 0; break; default: return 0; } return 1; } else return 0; } /**************************************************************************** ****************************************************************************/ static int is_myself(const unsigned char *mac, const unsigned char *px, unsigned length) { if (length < 12) return 0; if (memcmp(px+0, mac, 6) == 0) return 1; if (memcmp(px+6, mac, 6) == 0) return 1; return 0; } /**************************************************************************** ****************************************************************************/ void inbound_thread(void *v) { struct IsoWall *isowall = (struct IsoWall *)v; struct Adapter *src = &isowall->ex; struct Adapter *dst = &isowall->in; const struct RangeList *allowed = &isowall->allow; for (;;) { const unsigned char *px; unsigned length; int err; unsigned secs; unsigned usecs; /* * Receive a packet from the network */ err = rawsock_recv_packet( src->raw, &length, &secs, &usecs, &px); if (err != 0 || length == 0) continue; if (!isowall->is_reuse_external && is_myself(src->my_mac, px, length)) { LOG(0, "*** DANGER *** DANGER *** DANGER ***\n"); LOG(0, " A packet was discovered with MAC address of the adapter, \n"); LOG(0, " indicating that some other software has started using this \n"); LOG(0, " network. You are now exposed to the infected machine. Isowall \n"); LOG(0, " is shutting down\n"); LOG(0, "*** DANGER *** DANGER *** DANGER ***\n"); exit(1); } if (is_valid(px, length, src, dst, allowed, 1)) { src->stats.allowed++; rawsock_send_packet(dst->raw, px, length, 1); } else src->stats.blocked++; } } /**************************************************************************** * Same as inbound thread, but src/dst reversed, and is_inbound parameter * reversed. I need to make these two the same function. ****************************************************************************/ void outbound_thread(void *v) { struct IsoWall *isowall = (struct IsoWall *)v; struct Adapter *src = &isowall->in; struct Adapter *dst = &isowall->ex; const struct RangeList *allowed = &isowall->allow; for (;;) { const unsigned char *px; unsigned length; int err; unsigned secs; unsigned usecs; /* * Receive a packet from the network */ err = rawsock_recv_packet( src->raw, &length, &secs, &usecs, &px); if (err != 0 || length == 0) continue; if (!isowall->is_reuse_internal && is_myself(src->my_mac, px, length)) { LOG(0, "*** DANGER *** DANGER *** DANGER ***\n"); LOG(0, " A packet was discovered with MAC address of the adapter, \n"); LOG(0, " indicating that some other software has started using this \n"); LOG(0, " network. You are now exposed to the infected machine. Isowall \n"); LOG(0, " is shutting down\n"); LOG(0, "*** DANGER *** DANGER *** DANGER ***\n"); exit(1); } if (is_valid(px, length, src, dst, allowed, 0)) { src->stats.allowed++; rawsock_send_packet(dst->raw, px, length, 1); } else src->stats.blocked++; } } /**************************************************************************** ****************************************************************************/ static void isowall_bridge(struct IsoWall *isowall) { unsigned x; struct RawAdapter *raw; unsigned char *mac; /* * Combine the "white-list" and "blacklist" into a single set * of "whitelist" ranges. */ rangelist_exclude(&isowall->allow, &isowall->block); if (rangelist_count(&isowall->allow) == 0) { LOG(0, "FAIL: everything is blocked\n"); LOG(0, "...hint: do something like '--allow 0.0.0.0/0'\n"); LOG(0, "...hint: don't do something like '--block 0.0.0.0/0'\n"); exit(1); } /* * Make sure the user has specified both network adapters */ if (isowall->in.ifname == NULL || isowall->in.ifname[0] == '\0') { if (!isowall->is_reuse_internal) { LOG(0, "FAIL: no internal network adapter specified\n"); LOG(0, "...hint: do something like '--internal eth1'\n"); LOG(0, "...info: internal network is the isolated side\n"); exit(1); } } if (isowall->ex.ifname == NULL || isowall->ex.ifname[0] == '\0') { if (isowall->is_reuse_external) { /* no adapter specified, so find a default one */ int err; char ifname2[256]; size_t len; ifname2[0] = '\0'; err = rawsock_get_default_interface(ifname2, sizeof(ifname2)); if (err || ifname2[0] == '\0') { fprintf(stderr, "FAIL: could not determine default external interface\n"); fprintf(stderr, "...hint: try something like \"--external eth2\"\n"); exit(1); } else { LOG(0, "external-ifname = %s\n", ifname2); } len = strlen(ifname2) + 1; isowall->ex.ifname = (char*)malloc(len); if (isowall->ex.ifname) strcpy_s(isowall->ex.ifname, len, ifname2); } else { LOG(0, "FAIL: no external network adapter specified\n"); LOG(0, "...hint: do something like '--external eth2'\n"); LOG(0, "...info: external network is the side exposed to Internet\n"); exit(1); } } /* * Make sure we know the IP addresses of the internal target and the * external router */ if (isowall->in.target_ip == 0) { LOG(0, "FAIL: the IP address of the internal isolated machine not specified\n"); LOG(0, "...hint: do something like '--internal-target-ip 10.0.0.137'\n"); exit(1); } if (isowall->ex.target_ip == 0) { if (isowall->is_reuse_external) { int err; unsigned ip; err = rawsock_get_default_gateway( isowall->ex.ifname, &ip); if (err || ip == 0) { LOG(0, "FAIL: could not discover external router\n"); LOG(0, "...hint: do something like '--external-router-ip 10.0.0.137'\n"); exit(1); } else { LOG(0, "external-router-ip = %u.%u.%u.%u\n", (unsigned char)(ip>>24), (unsigned char)(ip>>16), (unsigned char)(ip>> 8), (unsigned char)(ip>> 0) ); isowall->ex.target_ip = ip; } } else { LOG(0, "FAIL: the IP address of the external router not specified\n"); LOG(0, "...hint: do something like '--external-router-ip 10.0.0.137'\n"); exit(1); } } /* * Make sure we don't have a network stack bound to the adapters */ x = rawsock_get_adapter_ip(isowall->in.ifname); if (x != 0 && !isowall->is_reuse_internal) { LOG(0, "FAIL: there is an IP address bound to the adapter\n"); LOG(0, "FAIL: '%s' has IPv4 address %u.%u.%u.%u\n", isowall->in.ifname, (unsigned char)(x>>24), (unsigned char)(x>>16), (unsigned char)(x>> 8), (unsigned char)(x>> 0) ); LOG(0, "...hint: THIS IS THE MOST IMPORTANT THING TO FULLY UNDERSTAND ABOUT THIS PROGRAM\n"); exit(1); } if (!isowall->is_reuse_external) { x = rawsock_get_adapter_ip(isowall->ex.ifname); if (x != 0) { LOG(0, "FAIL: there is an IP address bound to the adapter\n"); LOG(0, "FAIL: '%s' has IPv4 address %u.%u.%u.%u\n", isowall->in.ifname, (unsigned char)(x>>24), (unsigned char)(x>>16), (unsigned char)(x>> 8), (unsigned char)(x>> 0) ); LOG(0, "...hint: THIS IS THE MOST IMPORTANT THING TO FULLY UNDERSTAND ABOUT THIS PROGRAM\n"); exit(1); } } /* * Make sure we know the MAC address of the adapters */ mac = isowall->in.my_mac; if (memcmp(mac, "\0\0\0\0\0\0", 6) == 0) { rawsock_get_adapter_mac(isowall->in.ifname, mac); LOG(2, "auto-detected: adapter-mac=%02x-%02x-%02x-%02x-%02x-%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); } if (memcmp(mac, "\0\0\0\0\0\0", 6) == 0) { fprintf(stderr, "FAIL: failed to detect MAC address of interface: \"%s\"\n", isowall->in.ifname); fprintf(stderr, "...hint: try something like \"--internal-my-mac 00-11-22-33-44-55\"\n"); exit(1); } mac = isowall->ex.my_mac; if (memcmp(mac, "\0\0\0\0\0\0", 6) == 0) { rawsock_get_adapter_mac(isowall->ex.ifname, mac); LOG(2, "auto-detected: adapter-mac=%02x-%02x-%02x-%02x-%02x-%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); } if (memcmp(mac, "\0\0\0\0\0\0", 6) == 0 && !isowall->is_reuse_external) { fprintf(stderr, "FAIL: failed to detect MAC address of interface: \"%s\"\n", isowall->in.ifname); fprintf(stderr, "...hint: try something like \"--external-my-mac 00-11-22-33-44-55\"\n"); exit(1); } /* * Open the adapters */ raw = rawsock_init_adapter( isowall->in.ifname, isowall->is_pfring, isowall->is_sendq, isowall->is_packet_trace, isowall->is_offline, isowall->in.bpf_filter); if (raw == 0) { LOG(0, "FAIL: could not open internal adapter: %s\n", isowall->in.ifname); exit(1); } isowall->in.raw = raw; raw = rawsock_init_adapter( isowall->ex.ifname, isowall->is_pfring, isowall->is_sendq, isowall->is_packet_trace, isowall->is_offline, isowall->in.bpf_filter); if (raw == 0) { LOG(0, "FAIL: could not open internal adapter: %s\n", isowall->ex.ifname); exit(1); } isowall->ex.raw = raw; /* * If we have to, ARP the target IP addresses */ mac = isowall->in.target_mac; if (memcmp(mac, "\0\0\0\0\0\0", 6) == 0) { LOG(1, "rawsock: looking for target MAC address\n"); arp_resolve_sync( isowall->in.raw, isowall->ex.target_ip, /* spoof the address of other side */ isowall->ex.target_mac, isowall->in.target_ip, mac); if (memcmp(mac, "\0\0\0\0\0\0", 6) != 0) { LOG(0, "internal-target-mac = %02x-%02x-%02x-%02x-%02x-%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } else { LOG(0, "FAIL: couldn't find target MAC address\n"); LOG(0, "...hint: there is probably a networking error\n"); LOG(0, "...hint: you can manually config with '--internal-target-mac 66:55:44:33:22:11'\n"); } } mac = isowall->ex.target_mac; if (memcmp(mac, "\0\0\0\0\0\0", 6) == 0) { LOG(1, "rawsock: looking for default gateway\n"); arp_resolve_sync( isowall->ex.raw, isowall->in.target_ip, /* spoof the address of other side */ isowall->in.target_mac, isowall->ex.target_ip, mac); if (memcmp(mac, "\0\0\0\0\0\0", 6) != 0) { LOG(0, "external-router-mac = %02x-%02x-%02x-%02x-%02x-%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } else { LOG(0, "FAIL: couldn't find target MAC address\n"); LOG(0, "...hint: there is probably a networking error\n"); LOG(0, "...hint: you can manually config with '--external-router-mac 66:55:44:33:22:11'\n"); } } pixie_begin_thread(inbound_thread, 0, isowall); pixie_begin_thread(outbound_thread, 0, isowall); /* Print continuous status messages */ for (;;) { #if defined(WIN32) Sleep(1000); #else sleep(1); #endif fprintf(stderr, "inbound[drop=%-8lluforward=%-8llu] outbound[drop=%-8uforward=%-8llu]\r", isowall->ex.stats.blocked, isowall->ex.stats.allowed, isowall->in.stats.blocked, isowall->in.stats.allowed); } } /**************************************************************************** ****************************************************************************/ int main(int argc, char *argv[]) { struct IsoWall isowall[1]; memset(isowall, 0, sizeof(*isowall)); /* * On non-Windows systems, there might be a configuration * file in the well-known "/etc" location */ #if !defined(WIN32) if (access("/etc/isowall.conf", 0) == 0) { isowall_read_config_file(isowall, "/etc/isowall.conf"); } #endif /* * Read in the configuration from the command-line. */ isowall_command_line(isowall, argc, argv); /* * Once we've read in the configuration, do the operation that was * specified */ switch (isowall->op) { case Operation_Default: case Operation_Bridge: /* * THIS IS THE NORMAL THING */ isowall_bridge(isowall); return 0; case Operation_List_Adapters: /* List the network adapters we might want to use for scanning */ rawsock_list_adapters(); break; case Operation_DebugIF: printf("== INTERNAL ==\n"); rawsock_selftest_if(isowall->in.ifname, isowall->is_reuse_internal); printf("\n== EXTERNAL ==\n"); rawsock_selftest_if(isowall->ex.ifname, isowall->is_reuse_external); return 0; case Operation_Selftest: /* * Do a regression test of all the significant units */ { int x = 0; x += rawsock_selftest(); x += ranges_selftest(); if (x != 0) { /* one of the selftests failed, so return error */ fprintf(stderr, "regression test: failed :( \n"); return 1; } else { fprintf(stderr, "regression test: success!\n"); return 0; } } break; } return 0; }