#include #include #include #include #include #include #include #include "rserv.h" #include "flow.h" #include "slitheen.h" #include "util.h" #include "relay.h" void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet); void check_handshake(struct packet_info *info, flow f); void *sniff_packets(void *); void process_packet(struct packet_info *info); void extract_packet_headers(uint8_t *packet, struct packet_info *info); /** Checks a handshake message to see if it is tagged or a * recognized flow. * Inputs: The tcp header for the message, its associated flow, * a pointer to the handshake header, and the length of * the record. */ void check_handshake(struct packet_info *info, flow f){ FILE *fp; int res, i, code; uint8_t *hello_rand; const struct handshake_header *handshake_hdr; byte privkey[PTWIST_BYTES]; byte key[16]; uint8_t *p = info->app_data + RECORD_HEADER_LEN; handshake_hdr = (struct handshake_header*) p; code = handshake_hdr->type; if (code == 0x01){ p += CLIENT_HELLO_HEADER_LEN; //now pointing to hello random :D hello_rand = p; p += 4; //skipping time bytes /* Load the private key */ fp = fopen("privkey", "rb"); if (fp == NULL) { perror("fopen"); exit(1); } res = fread(privkey, PTWIST_BYTES, 1, fp); if (res < 1) { perror("fread"); exit(1); } fclose(fp); /* check tag*/ res = check_tag(key, privkey, p, (const byte *)"context", 7); if (res) { printf("Untagged\n"); } else { fp = fopen("tags", "wb"); if (fp == NULL) { perror("fopen"); exit(1); } //Write tag to file for(i=0; i< 28; i++){ fprintf(fp, "%02x ", p[i]); } fclose(fp); //Write key to file fp = fopen("sharedkey", "wb"); if (fp == NULL) { perror("fopen"); exit(1); } for(i=0; i<16;i++){ fprintf(fp, "%02x", key[i]); } fclose(fp); /* Save flow in table */ flow *flow_ptr = add_flow(f); for(int i=0; i<16; i++){ flow_ptr->key[i] = key[i]; } memcpy(flow_ptr->client_random, hello_rand, SSL3_RANDOM_SIZE); printf("Saved new flow\n"); } } } void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet){ pcap_t *handle = (pcap_t *) args; //might modify packet, copy to new pointer //uint8_t *modified_packet = calloc(1, header->len); //memcpy(modified_packet, packet, header->len); struct packet_info *info = calloc(1, sizeof(struct packet_info)); uint8_t *tmp_packet = calloc(1, header->len); memcpy(tmp_packet, packet, header->len); extract_packet_headers(tmp_packet, info); // Check to make sure it is an IP packet if(info->ip_hdr == NULL) goto end; /* Packet may be split over multiple frames. In this case, // reconstruct the packet before trying to read TLS records if((htons(info->ip_hdr->flagsoff)&MF) || (htons(info->ip_hdr->flagsoff) &0x1fff) ){ // this is a fragment printf("MF: %d, OFF: %d.\n", htons(info->ip_hdr->flagsoff)&MF, htons(info->ip_hdr->flagsoff)&0x1fff); printf("Received packet fragment.\n"); u_char *complete_packet; //save packet fragment complete_packet = malloc(65535); int success = report_fragment(p, complete_packet, header->len); if(success){ process_packet(complete_packet, info); } // TODO: handle errors } else { //not a fragment, add to packet chain */ process_packet(info); end: if((pcap_inject(handle, tmp_packet, header->len)) < 0 ){ fprintf(stderr, "Error: %s\n", pcap_geterr(handle)); } free(info);//Note: don't free this while a thread is using it #ifdef DEBUG fprintf(stderr, "injected the following packet:\n"); for(int i=0; i< header->len; i++){ fprintf(stderr, "%02x ", packet[i]); } fprintf(stderr, "\n"); #endif //free(modified_packet); } void usage(void){ printf("Usage: slitheen-proxy [internal network interface] [NAT interface]\n"); } int main(int argc, char *argv[]){ pthread_t t1, t2; char filter1[33] = "ether src host 08:00:27:e8:9d:d4"; char filter2[33] = "ether src host 08:00:27:e8:9d:d4"; char *dev1 = NULL; /* Device that leads to the internal network */ char *dev2 = NULL; /* Device that leads out to the world */ struct sniff_args outbound; struct sniff_args inbound; if (argc != 3) { usage(); return(2); } dev1 = argv[1]; dev2 = argv[2]; snprintf(filter1, 33, "ether src host %s", macaddr); snprintf(filter2, 33, "ether dst host %s", macaddr); init_flow_table(); init_fragment_table(); /* Create threads */ outbound.readdev = dev1; outbound.writedev = dev2; outbound.filter = filter1; inbound.readdev = dev2; inbound.writedev = dev1; inbound.filter = filter2; pthread_create(&t1, NULL, sniff_packets, (void *) &outbound); pthread_create(&t2, NULL, sniff_packets, (void *) &inbound); pthread_join(t1, NULL); pthread_join(t2, NULL); return(0); } void *sniff_packets(void *args){ pcap_t *rd_handle; pcap_t *wr_handle; char rd_errbuf[BUFSIZ]; char wr_errbuf[BUFSIZ]; struct bpf_program fp; bpf_u_int32 mask; bpf_u_int32 net; char *readdev, *writedev, *filter; struct sniff_args *arg_st = (struct sniff_args *) args; readdev = arg_st->readdev; writedev = arg_st->writedev; filter = arg_st->filter; if (pcap_lookupnet(readdev, &net, &mask, rd_errbuf) == -1){ fprintf(stderr, "Can't get netmask for device %s\n", readdev); exit(2); } rd_handle = pcap_open_live(readdev, BUFSIZ, 1, 0, rd_errbuf); if (rd_handle == NULL){ fprintf(stderr, "Couldn't open device %s: %s\n", readdev, rd_errbuf); } if(pcap_datalink(rd_handle) != DLT_EN10MB) { fprintf(stderr, "Device %s does not provide Ethernet headers - not supported\n", readdev); exit(2); } if(pcap_compile(rd_handle, &fp, filter, 0 , net) == -1){ fprintf(stderr, "Couldn't parse filter %s: %s\n", filter, pcap_geterr(rd_handle)); exit(2); } if (pcap_setfilter(rd_handle, &fp) == -1) { fprintf(stderr, "Couldn't install filter %s: %s\n", filter, pcap_geterr(rd_handle)); exit(2); } wr_handle = pcap_open_live(writedev, BUFSIZ, 1, 0, wr_errbuf); if (wr_handle == NULL){ fprintf(stderr, "Couldn't open device %s: %s\n", writedev, wr_errbuf); } /*callback function*/ pcap_loop(rd_handle, -1, got_packet, (unsigned char *) wr_handle); /*Sniff a packet*/ pcap_close(rd_handle); return NULL; } /* This function receives a full ip packet and then: * 1) identifies the flow * 2) adds the packet to the flow's data chain * 3) updates the flow's state */ void process_packet(struct packet_info *info){ int index; flow newFlow; if (info->tcp_hdr != NULL){ newFlow.src_ip = info->ip_hdr->src; newFlow.dst_ip = info->ip_hdr->dst; newFlow.src_port = info->tcp_hdr->src_port; newFlow.dst_port = info->tcp_hdr->dst_port; newFlow.seq_num = info->tcp_hdr->sequence_num; /* Checks to see if this is a possibly tagged hello msg */ if ((info->record_hdr != NULL) && (info->record_hdr->type == HS)){ /* This is a TLS handshake */ check_handshake(info, newFlow); } } /* Now if flow is in table, update state */ if((index = check_flow(newFlow))){ flow *observed = get_flow(index-1); if(observed->application){ replace_packet(observed, info); } else { /* Pass data to packet chain */ add_packet(observed, info); /* Update flow state */ if(observed->packet_chain != NULL){ update_flow(observed); } } /* Update TCP state */ if(info->tcp_hdr->flags & (FIN | RST) ){ /* Remove flow from table, connection ended */ remove_flow(index); } } } /** This function extracts the ip, tcp, and tls record headers * from a received packet (if they exist), and put them in * a packet_info struct * */ void extract_packet_headers(uint8_t *packet, struct packet_info *info){ /* First fill in IP header */ uint8_t *p = packet; p += ETHER_HEADER_LEN; //skip ethernet header info->ip_hdr = (struct ip_header*) p; info->size_ip_hdr = IP_HEADER_LEN(info->ip_hdr); /* Verify this is an IP packet */ if( (info->ip_hdr->versionihl >>4) != 4){ info->ip_hdr = NULL; info->size_ip_hdr = 0; info->tcp_hdr = NULL; info->size_tcp_hdr = 0; info->record_hdr = NULL; return; } /* If this is a TCP segment, fill in TCP header */ if (info->ip_hdr->proto == IPPROTO_TCP){ p += info->size_ip_hdr; //skip IP header info->tcp_hdr = (struct tcp_header*) p; info->size_tcp_hdr = TCP_HEADER_LEN(info->tcp_hdr); p += info->size_tcp_hdr; } else { info->tcp_hdr = NULL; info->size_tcp_hdr = 0; info->record_hdr = NULL; return; } /* If the application data contains a TLS record, fill in hdr */ info->app_data_len = htons(info->ip_hdr->len) - (info->size_ip_hdr + info->size_tcp_hdr); if(info->app_data_len > 0){ info->app_data = p; info->record_hdr = (struct tls_header*) p; //check to see if this is a valid record if((info->record_hdr->type < 0x14) || (info->record_hdr->type > 0x18)){ info->record_hdr = NULL; } } else { info->record_hdr = NULL; info->app_data = NULL; } return; }