|
@@ -8,6 +8,11 @@
|
|
|
#include <netdb.h>
|
|
|
#include <unistd.h>
|
|
|
#include <pthread.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+#include <openssl/bio.h>
|
|
|
+#include <openssl/evp.h>
|
|
|
+
|
|
|
#include "relay.h"
|
|
|
#include "slitheen.h"
|
|
|
#include "flow.h"
|
|
@@ -58,7 +63,7 @@ int replace_packet(flow *f, struct packet_info *info){
|
|
|
f->seq_num += info->app_data_len;
|
|
|
/* if incoming, replace with data from queue */
|
|
|
//if(htonl(tcp_hdr->sequence_num) >= f->seq_num){
|
|
|
- replace_contents(f, offset, info);
|
|
|
+ process_downstream(f, offset, info);
|
|
|
//}//TODO: need to do something about replaying packets (maybe store previously sent data??
|
|
|
|
|
|
|
|
@@ -79,10 +84,13 @@ int replace_packet(flow *f, struct packet_info *info){
|
|
|
}
|
|
|
|
|
|
/** Reads the HTTP header of upstream data and searches for
|
|
|
- * a covert request in the x-ignore header. Sends this
|
|
|
+ * a covert request in an x-slitheen header. Sends this
|
|
|
* request to the indicated site and saves the response to
|
|
|
* the censored queue
|
|
|
*
|
|
|
+ * TODO: change this to take SOCKS5 proxy connection and then relay data
|
|
|
+ * based on stream ID
|
|
|
+ *
|
|
|
* Inputs:
|
|
|
* f: the tagged flow
|
|
|
* info: the processed received packet
|
|
@@ -107,8 +115,10 @@ int read_header(flow *f, struct packet_info *info){
|
|
|
memcpy(decrypted_data, p, record_length);
|
|
|
|
|
|
if(!encrypt(f, decrypted_data, decrypted_data, record_length, 0, record_hdr->type, 0)){
|
|
|
- fprintf(stdout,"decryption failed\n");
|
|
|
+ fprintf(stdout,"upstream decryption failed\n");
|
|
|
return 0;
|
|
|
+ } else {
|
|
|
+ fprintf(stdout, "upstream decryption succeeded\n");
|
|
|
}
|
|
|
|
|
|
if(record_hdr->type == 0x15){
|
|
@@ -120,64 +130,62 @@ int read_header(flow *f, struct packet_info *info){
|
|
|
}
|
|
|
|
|
|
//TODO: re-write this to take a SOCKS connection request
|
|
|
-
|
|
|
/* search through decrypted data for x-ignore */
|
|
|
- regex_t r;
|
|
|
- const char *regex_text = "x-ignore";
|
|
|
- const char *match = (char *) decrypted_data;
|
|
|
- if(regcomp(&r, regex_text, REG_EXTENDED|REG_NEWLINE)){
|
|
|
- printf("could not compile regex\n");
|
|
|
+ char *header_ptr = strstr((const char *) decrypted_data, "X-Slitheen");
|
|
|
+ uint8_t *upstream_data;
|
|
|
+ if(header_ptr == NULL){
|
|
|
+ printf("UPSTREAM: No x-slitheen header found\n");
|
|
|
return 0;
|
|
|
}
|
|
|
-
|
|
|
- regmatch_t m;
|
|
|
- if(regexec(&r, match, 1, &m, 0)){
|
|
|
- printf("Upstream data: no x-ignore header\n");
|
|
|
- return 0;
|
|
|
- }
|
|
|
- uint8_t *message = decrypted_data;
|
|
|
- //remove escape characters
|
|
|
- for(int i=m.rm_eo+2; i< strlen(match); i++){
|
|
|
- if(match[i] != '\\'){
|
|
|
- *(message++) = match[i];
|
|
|
- } else if (match[i+1] == 'r') {
|
|
|
- *(message++) = '\r';
|
|
|
- i++;
|
|
|
- } else if (match[i+1] == 'n') {
|
|
|
- *(message++) = '\n';
|
|
|
- i++;
|
|
|
- }
|
|
|
- }
|
|
|
- *message = '\0';
|
|
|
- message = decrypted_data;
|
|
|
-
|
|
|
- regex_t r2;
|
|
|
- const char *regex_text2 = "host";
|
|
|
- const char *match2 = (char *) decrypted_data;
|
|
|
- regmatch_t m2;
|
|
|
- if(regcomp(&r2, regex_text2, REG_EXTENDED|REG_NEWLINE)){
|
|
|
- printf("could not compile regex\n");
|
|
|
- return 0;
|
|
|
+ printf("UPSTREAM: Found x-slitheen header\n");
|
|
|
+ fprintf(stdout,"UPSTREAM Flow: %x > %x (%s)\n", info->ip_hdr->src.s_addr, info->ip_hdr->dst.s_addr, (info->ip_hdr->src.s_addr != f->src_ip.s_addr)? "incoming":"outgoing");
|
|
|
+ fprintf(stdout, "Sequence number: %d\n", info->tcp_hdr->sequence_num);
|
|
|
+
|
|
|
+ header_ptr += strlen("X-Slitheen: ");
|
|
|
+ char *c = header_ptr;
|
|
|
+ while(*c != '\r'){
|
|
|
+ c++;
|
|
|
}
|
|
|
- if(regexec(&r2, match2, 1, &m2, 0)){
|
|
|
- printf("no host found\n");
|
|
|
- return 0;
|
|
|
+ c++;
|
|
|
+ *c = '\0';
|
|
|
+ printf("upstream data: %s\n", header_ptr);
|
|
|
+
|
|
|
+ //b64 decode the data
|
|
|
+ int32_t decode_len = strlen(header_ptr);
|
|
|
+ if(header_ptr[decode_len-2] == '='){
|
|
|
+ decode_len = decode_len*3/4 - 2;
|
|
|
+ } else if(header_ptr[decode_len-1] == '='){
|
|
|
+ decode_len = decode_len*3/4 - 1;
|
|
|
+ } else {
|
|
|
+ decode_len = decode_len*3/4;
|
|
|
}
|
|
|
- uint8_t server[50];
|
|
|
- for(int i=m2.rm_eo+2; i< strlen(match2); i++){
|
|
|
- if(match2[i] != '\n'){
|
|
|
- server[i-m2.rm_eo-2] = match2[i];
|
|
|
- } else {
|
|
|
- server[i-m2.rm_eo-2] = '\0';
|
|
|
- break;
|
|
|
- }
|
|
|
+
|
|
|
+ upstream_data = calloc(1, decode_len + 1);
|
|
|
+
|
|
|
+ BIO *bio, *b64;
|
|
|
+ bio = BIO_new_mem_buf(header_ptr, -1);
|
|
|
+ b64 = BIO_new(BIO_f_base64());
|
|
|
+ bio = BIO_push(b64, bio);
|
|
|
+ BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
|
|
|
+
|
|
|
+ int32_t output_len = BIO_read(bio, upstream_data, strlen(header_ptr));
|
|
|
+ if(output_len != decode_len)
|
|
|
+ printf("UH OH, lens dont match\n");
|
|
|
+
|
|
|
+ printf("Decoded to get %d bytes:\n", output_len);
|
|
|
+ for(int i=0; i< output_len; i++){
|
|
|
+ printf("%02x ", upstream_data[i]);
|
|
|
}
|
|
|
+ printf("\n");
|
|
|
+ uint8_t stream_id = upstream_data[0];
|
|
|
+ p = upstream_data +1;
|
|
|
+
|
|
|
+ printf("Received bytes with stream id %d\n", stream_id);
|
|
|
|
|
|
- printf("Collecting data from censored site: %s\n", server);
|
|
|
- //If a thread for this stream id exists, get the thread info
|
|
|
- //TODO: fill this in
|
|
|
+ //If a thread for this stream id exists, get the thread info and pipe data
|
|
|
+ //TODO: fill this in
|
|
|
|
|
|
- //Else, spawn a thread to handle the proxy to this site
|
|
|
+ /*Else, spawn a thread to handle the proxy to this site*/
|
|
|
pthread_t *proxy_thread = calloc(1, sizeof(pthread_t));
|
|
|
int32_t pipefd[2];
|
|
|
if(pipe(pipefd) < 0){
|
|
@@ -187,26 +195,28 @@ int read_header(flow *f, struct packet_info *info){
|
|
|
|
|
|
struct proxy_thread_data *thread_data =
|
|
|
calloc(1, sizeof(struct proxy_thread_data));
|
|
|
+ thread_data->initial_data = upstream_data;
|
|
|
thread_data->f = f;
|
|
|
- memcpy(thread_data->server, server, 50);
|
|
|
- printf("Collecting data from censored site: %s\n", thread_data->server);
|
|
|
thread_data->pipefd = pipefd[0];
|
|
|
|
|
|
pthread_create(proxy_thread, NULL, proxy_covert_site, (void *) thread_data);
|
|
|
|
|
|
- //save a reference to the proxy threads in the flow
|
|
|
+ //save a reference to the proxy threads in a global table
|
|
|
|
|
|
|
|
|
- //Now pipe information to the selected or created thread
|
|
|
- //TODO: message may not be a string. fix.
|
|
|
- int32_t bytes_written = write(pipefd[1], message,
|
|
|
+ /*int32_t bytes_written = write(pipefd[1], message,
|
|
|
strlen( (const char *) message));
|
|
|
if(bytes_written < strlen( (const char *) message)){
|
|
|
printf("failed to write all bytes to pipe\n");
|
|
|
- }
|
|
|
+ }*/
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
+err:
|
|
|
+ if(upstream_data != NULL){
|
|
|
+ free(upstream_data);
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
/** Called by spawned pthreads in read_header to send upstream
|
|
@@ -218,7 +228,7 @@ int read_header(flow *f, struct packet_info *info){
|
|
|
* Input:
|
|
|
* A struct that contains the following information:
|
|
|
* - the tagged flow
|
|
|
- * - the name of the server
|
|
|
+ * - the initial upstream data (including connect request)
|
|
|
* - the read end of the pipe
|
|
|
*
|
|
|
*/
|
|
@@ -226,42 +236,113 @@ void *proxy_covert_site(void *data){
|
|
|
|
|
|
struct proxy_thread_data *thread_data =
|
|
|
(struct proxy_thread_data *) data;
|
|
|
-
|
|
|
flow *f = thread_data->f;
|
|
|
|
|
|
- int32_t buffer_len = BUFSIZ;
|
|
|
- uint8_t *buffer = calloc(1, BUFSIZ);
|
|
|
- /* Send GET request to site */
|
|
|
- printf("Collecting data from censored site: %s\n", thread_data->server);
|
|
|
+ uint8_t *p = thread_data->initial_data;
|
|
|
+ uint8_t stream_id = p[0];
|
|
|
+ p++;
|
|
|
+ struct socks_req *clnt_req = (struct socks_req *) p;
|
|
|
+ p += 4;
|
|
|
|
|
|
- struct hostent *host;
|
|
|
- host = gethostbyname((const char *) thread_data->server);
|
|
|
+ //see if it's a connect request
|
|
|
+ if(clnt_req->cmd != 0x01){
|
|
|
+ printf("Error: issued a non-connect command\n");
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
|
|
|
- if(host == NULL){
|
|
|
- printf("gethostbyname failed\n");
|
|
|
- return 0;
|
|
|
+ struct sockaddr_in dest;
|
|
|
+ dest.sin_family = AF_INET;
|
|
|
+ uint8_t domain_len;
|
|
|
+
|
|
|
+ switch(clnt_req->addr_type){
|
|
|
+ case 0x01:
|
|
|
+ //IPv4
|
|
|
+ dest.sin_addr.s_addr = *((uint32_t*) p);
|
|
|
+ printf("destination addr: %d\n", ntohl(dest.sin_addr.s_addr));
|
|
|
+ p += 4;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 0x03:
|
|
|
+ //domain name
|
|
|
+ domain_len = p[0];
|
|
|
+ p++;
|
|
|
+ uint8_t *domain_name = calloc(1, domain_len+1);
|
|
|
+ memcpy(domain_name, p, domain_len);
|
|
|
+ domain_name[domain_len] = '\0';
|
|
|
+ struct hostent *host;
|
|
|
+ host = gethostbyname((const char *) domain_name);
|
|
|
+ dest.sin_addr = *((struct in_addr *) host->h_addr);
|
|
|
+ printf("destination addr: %d\n", ntohl(dest.sin_addr.s_addr));
|
|
|
+
|
|
|
+ p += domain_len;
|
|
|
+ free(domain_name);
|
|
|
+ break;
|
|
|
+ case 0x04:
|
|
|
+ //IPv6
|
|
|
+ goto err;//TODO: fix this
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
- int32_t handle = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
- if(handle < 0){
|
|
|
- printf("error: constructing socket failed\n");
|
|
|
- return 0;
|
|
|
+ //now set the port
|
|
|
+ dest.sin_port = *((uint16_t *) p);
|
|
|
+ printf("destination port: %d\n", ntohs(dest.sin_port));
|
|
|
+
|
|
|
+ int32_t handle = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
+ if(handle < 0){
|
|
|
+ printf("error: constructing socket failed\n");
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ struct sockaddr_in my_addr;
|
|
|
+ socklen_t my_addr_len = sizeof(my_addr);
|
|
|
+
|
|
|
+ int32_t error = connect (handle, (struct sockaddr *) &dest, sizeof (struct sockaddr));
|
|
|
+
|
|
|
+ if(error <0){
|
|
|
+ printf("error connecting\n");
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ getsockname(handle, (struct sockaddr *) &my_addr, &my_addr_len);
|
|
|
+
|
|
|
+ uint8_t *response = calloc(1, 11);
|
|
|
+ //now send the reply to the client
|
|
|
+ response[0] = 0x05;
|
|
|
+ response[1] = 0x00;//TODO: make this accurate
|
|
|
+ response[2] = 0x00;
|
|
|
+ response[3] = 0x01;
|
|
|
+ *((uint32_t *) (response + 4)) = my_addr.sin_addr.s_addr;
|
|
|
+ *((uint16_t *) (response + 8)) = my_addr.sin_port;
|
|
|
+
|
|
|
+ printf("Bound to %x:%d\n", my_addr.sin_addr.s_addr, ntohs(my_addr.sin_port));
|
|
|
+ printf("Downstream response:\n");
|
|
|
+ for(int i=0; i< 10; i++){
|
|
|
+ printf("%02x ", response[i]);
|
|
|
}
|
|
|
-
|
|
|
- struct sockaddr_in dest;
|
|
|
- dest.sin_family = AF_INET;
|
|
|
- dest.sin_port = htons(80);
|
|
|
- dest.sin_addr = *((struct in_addr *) host->h_addr);
|
|
|
- bzero (&(dest.sin_zero), 8);
|
|
|
-
|
|
|
- int32_t error = connect (handle, (struct sockaddr *) &dest, sizeof (struct sockaddr));
|
|
|
-
|
|
|
- if(error <0){
|
|
|
- printf("error connecting\n");
|
|
|
- return 0;
|
|
|
+ printf("\n");
|
|
|
+
|
|
|
+ queue_block *new_block = calloc(1, sizeof(queue_block));
|
|
|
+ printf("Inserting new block into queue: %p\n", new_block);
|
|
|
+ fflush(stdout);
|
|
|
+ new_block->len = 10;
|
|
|
+ new_block->offset = 0;
|
|
|
+ new_block->data = response;
|
|
|
+ new_block->next = NULL;
|
|
|
+ new_block->stream_id = stream_id;
|
|
|
+ printf("downstream_queue is at %p\n", downstream_queue);
|
|
|
+ fflush(stdout);
|
|
|
+ if(downstream_queue->first_block == NULL){
|
|
|
+ downstream_queue->first_block = new_block;
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ queue_block *last = downstream_queue->first_block;
|
|
|
+ while(last->next != NULL)
|
|
|
+ last = last->next;
|
|
|
+ last->next = new_block;
|
|
|
}
|
|
|
+ return 0; //slowly test
|
|
|
|
|
|
- //now select on reading from the pipe and from the socket
|
|
|
+ /*now select on reading from the pipe and from the socket
|
|
|
for(;;){
|
|
|
fd_set readfds;
|
|
|
fd_set writefds;
|
|
@@ -315,7 +396,7 @@ void *proxy_covert_site(void *data){
|
|
|
if(bytes_read > 0){
|
|
|
|
|
|
//make a new queue block
|
|
|
- queue_block *new_block = calloc(1, sizeof(queue_block));
|
|
|
+ new_block = calloc(1, sizeof(queue_block));
|
|
|
new_block->len = bytes_read;
|
|
|
new_block->offset = 0;
|
|
|
new_block->data = buf;
|
|
@@ -337,6 +418,13 @@ void *proxy_covert_site(void *data){
|
|
|
free(thread_data);
|
|
|
free(buffer);
|
|
|
close(handle);
|
|
|
+ */
|
|
|
+ return 0;
|
|
|
+err:
|
|
|
+ if(thread_data->initial_data != NULL){
|
|
|
+ free(thread_data->initial_data);
|
|
|
+ }
|
|
|
+ close(handle);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -355,14 +443,343 @@ void *proxy_covert_site(void *data){
|
|
|
* Output:
|
|
|
* Returns 0 on sucess
|
|
|
*/
|
|
|
-int replace_contents(flow *f, int32_t offset, struct packet_info *info){
|
|
|
+int process_downstream(flow *f, int32_t offset, struct packet_info *info){
|
|
|
+
|
|
|
+ printf("Processing downstream data\n");
|
|
|
+ uint8_t changed = 0;
|
|
|
+
|
|
|
+ uint8_t *p = info->app_data;
|
|
|
+ uint32_t remaining_packet_len = info->app_data_len;
|
|
|
+ printf("This packet has %d bytes of application data\n", remaining_packet_len);
|
|
|
+ printf("Sequence number: %u:%u\n", htonl(info->tcp_hdr->sequence_num), htonl(info->tcp_hdr->sequence_num)+ remaining_packet_len);
|
|
|
+ fflush(stdout);
|
|
|
+
|
|
|
+ if(f->remaining_record_len > 0){
|
|
|
+ //ignore bytes until the end of the record
|
|
|
+ if(f->remaining_record_len > remaining_packet_len){ //ignore entire packet
|
|
|
+ f->remaining_record_len -= remaining_packet_len;
|
|
|
+ printf("Ignoring %d bytes, %d bytes left to ignore\n", remaining_packet_len, f->remaining_record_len);
|
|
|
+ remaining_packet_len -= remaining_packet_len;
|
|
|
+ } else {
|
|
|
+ p += f->remaining_record_len;
|
|
|
+ printf("Ignoring %d bytes\n", f->remaining_record_len);
|
|
|
+ remaining_packet_len -= f->remaining_record_len;
|
|
|
+ f->remaining_record_len = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ printf("There are now %d bytes remaining\n", remaining_packet_len);
|
|
|
+ fflush(stdout);
|
|
|
+ while(remaining_packet_len > 0){ //while bytes remain in the packet
|
|
|
+
|
|
|
+ struct record_header *record_hdr = (struct record_header*) p;
|
|
|
+ uint32_t record_len = RECORD_LEN(record_hdr);
|
|
|
+ printf("Record length: %d\n", record_len);
|
|
|
+
|
|
|
+ fprintf(stdout, "Record:\n");
|
|
|
+ for(int i=0; i< RECORD_HEADER_LEN; i++){
|
|
|
+ printf("%02x ", p[i]);
|
|
|
+ }
|
|
|
+ printf("\n");
|
|
|
+
|
|
|
+ p += RECORD_HEADER_LEN;
|
|
|
+ remaining_packet_len -= RECORD_HEADER_LEN;
|
|
|
+
|
|
|
+ uint8_t *record_ptr = p; //points to the beginning of record data
|
|
|
+ uint32_t remaining_record_len = record_len;
|
|
|
+
|
|
|
+ if(record_len > remaining_packet_len){
|
|
|
+ //for now, just forfeit this record
|
|
|
+ printf("Record is too long\n");
|
|
|
+ f->remaining_record_len = record_len - remaining_packet_len;
|
|
|
+ remaining_packet_len -= remaining_packet_len;
|
|
|
+
|
|
|
+ if(f->httpstate == PARSE_HEADER || f->httpstate == BEGIN_CHUNK || f->httpstate == END_CHUNK){
|
|
|
+ f->httpstate = FORFEIT_REST;
|
|
|
+ } else if( f->httpstate == MID_CONTENT || f->httpstate == MID_CHUNK){
|
|
|
+ f->remaining_response_len -= record_len - 24; //len of IV and padding
|
|
|
+ printf("remaining response len: %d\n", f->remaining_response_len);
|
|
|
+ if(f->remaining_response_len == 0){
|
|
|
+ if(f->httpstate == MID_CHUNK)
|
|
|
+ f->httpstate = END_CHUNK;
|
|
|
+ else
|
|
|
+ f->httpstate = END_BODY;
|
|
|
+ }
|
|
|
+ if(f->remaining_response_len < 0){
|
|
|
+ f->remaining_response_len = 0;
|
|
|
+ f->httpstate = FORFEIT_REST;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //now decrypt the record
|
|
|
+ printf("Decrypting record\n");
|
|
|
+ int32_t n = encrypt(f, record_ptr, record_ptr, record_len, 1,
|
|
|
+ record_hdr->type, 0);
|
|
|
+ if(n < 0){
|
|
|
+ //do something smarter here
|
|
|
+ fprintf(stdout,"application decryption failed\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ changed = 1;
|
|
|
+ printf("Decryption succeeded\n");
|
|
|
+
|
|
|
+ printf("Bytes:\n");
|
|
|
+ for(int i=0; i< n; i++){
|
|
|
+ printf("%02x ", record_ptr[EVP_GCM_TLS_EXPLICIT_IV_LEN+i]);
|
|
|
+ }
|
|
|
+ printf("\n");
|
|
|
+ printf("Text:\n");
|
|
|
+ printf("%s\n", record_ptr+EVP_GCM_TLS_EXPLICIT_IV_LEN);
|
|
|
+
|
|
|
+ p += EVP_GCM_TLS_EXPLICIT_IV_LEN;
|
|
|
+ char *len_ptr, *needle;
|
|
|
+
|
|
|
+ remaining_record_len = n;
|
|
|
+
|
|
|
+ while(remaining_record_len > 0){
|
|
|
+
|
|
|
+ switch(f->httpstate){
|
|
|
+
|
|
|
+ case PARSE_HEADER:
|
|
|
+ //determine whether it's transfer encoded or otherwise
|
|
|
+ len_ptr = strstr((const char *) p, "Transfer-Encoding");
|
|
|
+ if(len_ptr != NULL){
|
|
|
+ if(!memcmp(len_ptr + 19, "chunked", 7)){
|
|
|
+ printf("SLITHEEN: transfer chunked\n");
|
|
|
+ //now find end of header
|
|
|
+
|
|
|
+ len_ptr = strstr((const char *) p, "\r\n\r\n");
|
|
|
+ if(len_ptr != NULL){
|
|
|
+ f->httpstate = BEGIN_CHUNK;
|
|
|
+ remaining_record_len -= (((uint8_t *)len_ptr - p) + 4);
|
|
|
+ p = (uint8_t *) len_ptr + 4;
|
|
|
+ printf("remaining record len: %d\n", remaining_record_len);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ len_ptr = strstr((const char *) p, "Content-Length");
|
|
|
+ if(len_ptr != NULL){
|
|
|
+ len_ptr += 15;
|
|
|
+ f->remaining_response_len = strtol((const char *) len_ptr, NULL, 10);
|
|
|
+ printf("SLITHEEN: Content-Length: %d\n", f->remaining_response_len);
|
|
|
+ len_ptr = strstr((const char *) p, "\r\n\r\n");
|
|
|
+ if(len_ptr != NULL){
|
|
|
+ f->httpstate = MID_CONTENT;
|
|
|
+ remaining_record_len -= (((uint8_t *)len_ptr - p) + 4);
|
|
|
+ p = (uint8_t *) len_ptr + 4;
|
|
|
+ printf("remaining record len: %d\n", remaining_record_len);
|
|
|
+ } else {
|
|
|
+ printf("UH OH\n");
|
|
|
+ remaining_record_len = 0;
|
|
|
+ f->httpstate = FORFEIT_REST;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ f->httpstate = FORFEIT_REST;
|
|
|
+ remaining_record_len = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //figure out what the content-type is
|
|
|
+ len_ptr = strstr((const char *) record_ptr, "Content-Type: image");
|
|
|
+ if(len_ptr != NULL){
|
|
|
+ printf("SLITHEEN: replaceable content\n");
|
|
|
+ f->replace_response = 1;
|
|
|
+ memcpy(len_ptr + 14, "slitheen", 8);
|
|
|
+ char *c = len_ptr + 14+8;
|
|
|
+ while(c[0] != '\r'){
|
|
|
+ c[0] = ' ';
|
|
|
+ c++;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ printf("SLITHEEN: non-replaceable content\n");
|
|
|
+ f->replace_response = 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MID_CONTENT:
|
|
|
+ //check if content is replaceable
|
|
|
+ printf("In MID_CONTENT\n");
|
|
|
+ if(f->remaining_response_len > remaining_record_len){
|
|
|
+ if(f->replace_response){
|
|
|
+ fill_with_downstream(f, p, remaining_record_len);
|
|
|
+ printf("Replaced with:\n");
|
|
|
+ for(int i=0; i< remaining_record_len; i++){
|
|
|
+ printf("%02x ", p[i]);
|
|
|
+ }
|
|
|
+ printf("\n");
|
|
|
+ }
|
|
|
+ f->remaining_response_len -= remaining_record_len;
|
|
|
+ p += remaining_record_len;
|
|
|
+
|
|
|
+ remaining_record_len = 0;
|
|
|
+ printf("%d bytes left of body\n", f->remaining_response_len);
|
|
|
+ } else {
|
|
|
+ if(f->replace_response){
|
|
|
+ fill_with_downstream(f, p, remaining_record_len);
|
|
|
+ printf("Replaced with:\n");
|
|
|
+ for(int i=0; i< remaining_record_len; i++){
|
|
|
+ printf("%02x ", p[i]);
|
|
|
+ }
|
|
|
+ printf("\n");
|
|
|
+ }
|
|
|
+ remaining_record_len -= f->remaining_response_len;
|
|
|
+ p += f->remaining_response_len;
|
|
|
+ f->httpstate = PARSE_HEADER;
|
|
|
+ f->remaining_response_len = 0;
|
|
|
+ printf("rem. response len = %d\n", f->remaining_response_len);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case BEGIN_CHUNK:
|
|
|
+ printf("In BEGIN_CHUNK\n");
|
|
|
+
|
|
|
+ int32_t chunk_size = strtol((const char *) p, NULL, 16);
|
|
|
+ printf("chunk size: %x\n", chunk_size);
|
|
|
+ if(chunk_size == 0){
|
|
|
+ f->httpstate = END_BODY;
|
|
|
+ } else {
|
|
|
+ f->httpstate = MID_CHUNK;
|
|
|
+ }
|
|
|
+ f->remaining_response_len = chunk_size;
|
|
|
+ needle = strstr((const char *) p, "\r\n");
|
|
|
+ if(needle != NULL){
|
|
|
+ remaining_record_len -= ((uint8_t *) needle - p + 2);
|
|
|
+ p = (uint8_t *) needle + 2;
|
|
|
+ printf("remaining_record len = %d\n", remaining_record_len);
|
|
|
+ } else {
|
|
|
+ printf("UH OH\n");
|
|
|
+ remaining_record_len = 0;
|
|
|
+ f->httpstate = FORFEIT_REST;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case MID_CHUNK:
|
|
|
+ printf("In MID_CHUNK\n");
|
|
|
+ if(f->remaining_response_len > remaining_record_len){
|
|
|
+ if(f->replace_response){
|
|
|
+ fill_with_downstream(f, p, remaining_record_len);
|
|
|
+ printf("Replaced with:\n");
|
|
|
+ for(int i=0; i< remaining_record_len; i++){
|
|
|
+ printf("%02x ", p[i]);
|
|
|
+ }
|
|
|
+ printf("\n");
|
|
|
+ }
|
|
|
+ f->remaining_response_len -= remaining_record_len;
|
|
|
+ p += remaining_record_len;
|
|
|
+
|
|
|
+ remaining_record_len = 0;
|
|
|
+ printf("%d bytes left of chunk\n", f->remaining_response_len);
|
|
|
+ } else {
|
|
|
+ if(f->replace_response){
|
|
|
+ fill_with_downstream(f, p, remaining_record_len);
|
|
|
+ printf("Replaced with:\n");
|
|
|
+ for(int i=0; i< remaining_record_len; i++){
|
|
|
+ printf("%02x ", p[i]);
|
|
|
+ }
|
|
|
+ printf("\n");
|
|
|
+ }
|
|
|
+ remaining_record_len -= f->remaining_response_len;
|
|
|
+ p += f->remaining_response_len;
|
|
|
+ f->httpstate = END_CHUNK;
|
|
|
+ printf("rem. record len = %d\n", f->remaining_record_len);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case END_CHUNK:
|
|
|
+ printf("In END_CHUNK\n");
|
|
|
+ needle = strstr((const char *) p, "\r\n");
|
|
|
+ if(needle != NULL){
|
|
|
+ f->httpstate = BEGIN_CHUNK;
|
|
|
+ p += 2;
|
|
|
+ remaining_record_len -= 2;
|
|
|
+ } else {
|
|
|
+ printf("UH OH\n");
|
|
|
+ remaining_record_len = 0;
|
|
|
+ f->httpstate = FORFEIT_REST;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case END_BODY:
|
|
|
+ printf("In END_BODY\n");
|
|
|
+ needle = strstr((const char *) p, "\r\n");
|
|
|
+ if(needle != NULL){
|
|
|
+ f->httpstate = PARSE_HEADER;
|
|
|
+ p += 2;
|
|
|
+ remaining_record_len -= 2;
|
|
|
+ } else {
|
|
|
+ printf("UH OH\n");
|
|
|
+ remaining_record_len = 0;
|
|
|
+ f->httpstate = FORFEIT_REST;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case FORFEIT_REST:
|
|
|
+ printf("In FORFEIT_REST\n");
|
|
|
+
|
|
|
+ case USE_REST:
|
|
|
+ remaining_record_len = 0;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+
|
|
|
+ if(f->remaining_response_len > 0){
|
|
|
+ if(f->ignore_response){
|
|
|
+ uint32_t skip_len = (f->remaining_response_len > remaining_record_len) ?
|
|
|
+ remaining_record_len : f->remaining_response_len;
|
|
|
+ p += skip_len;
|
|
|
+ remaining_record_len -= skip_len;
|
|
|
+ f->remaining_response_len -= skip_len;
|
|
|
+ } else {//replace with downstream data, for now ignore
|
|
|
+ uint32_t skip_len = (f->remaining_response_len > remaining_record_len) ?
|
|
|
+ remaining_record_len : f->remaining_response_len;
|
|
|
+ p += skip_len;
|
|
|
+ remaining_record_len -= skip_len;
|
|
|
+ f->remaining_response_len -= skip_len;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //cycle through all responses
|
|
|
+ while(remaining_record_len > 0){
|
|
|
+ uint32_t response_len = get_response_length(p);
|
|
|
|
|
|
- printf("Replacing contents of downstream data\n");
|
|
|
+
|
|
|
+ response_len = remaining_record_len;
|
|
|
+ remaining_record_len -= response_len;
|
|
|
+ p += response_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ */
|
|
|
+ printf("Re-encrypting record\n");
|
|
|
+ if((n = encrypt(f, record_ptr, record_ptr,
|
|
|
+ n + EVP_GCM_TLS_EXPLICIT_IV_LEN, 1, record_hdr->type,
|
|
|
+ 1)) < 0){
|
|
|
+ fprintf(stdout,"encryption failed\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ printf("Encryption successful!\n");
|
|
|
+
|
|
|
+ p = record_ptr + record_len;
|
|
|
+ remaining_packet_len -= record_len;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+/*
|
|
|
|
|
|
uint8_t *p = info->app_data;
|
|
|
|
|
|
int32_t tmp_len = info->app_data_len;
|
|
|
|
|
|
+ printf("remaining packet length: %d\n", tmp_len);
|
|
|
//step 1: replace record contents
|
|
|
//note: encrypted message will be original message size + EVP_GCM_TLS_EXPLICIT_IV_LEN + 16 byte pad
|
|
|
//first check to see if there's anything in the outbox
|
|
@@ -380,28 +797,61 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
- if(tmp_len >= f->outbox_len){
|
|
|
- memcpy(p, f->outbox, f->outbox_len);
|
|
|
-
|
|
|
- p += f->outbox_len;
|
|
|
- tmp_len -= f->outbox_len;
|
|
|
- f->outbox_len = 0;
|
|
|
- free(f->outbox);
|
|
|
+ //If the outbox contains data we can't change, just forward it as is.
|
|
|
+
|
|
|
+ if(f->outbox_data_len > 0){
|
|
|
+ if(tmp_len >= f->outbox_data_len){
|
|
|
+ printf("There are %d bytes in the outbox\n", f->outbox_data_len);
|
|
|
+ memcpy(p, f->outbox, f->outbox_data_len);
|
|
|
+
|
|
|
+ p += f->outbox_data_len;
|
|
|
+ tmp_len -= f->outbox_data_len;
|
|
|
+ f->outbox_len -= f->outbox_data_len;
|
|
|
+ f->outbox_data_len = 0;
|
|
|
+ free(f->outbox);
|
|
|
+
|
|
|
+ //now see if there are remainders of the encrypted record to send...
|
|
|
+ if(tmp_len > 0){
|
|
|
+ if(tmp_len >= f->outbox_len){
|
|
|
+ p += f->outbox_len;
|
|
|
+ tmp_len -= f->outbox_len;
|
|
|
+ f->outbox_len = 0;
|
|
|
+ } else {
|
|
|
+ f->outbox_len -= tmp_len;
|
|
|
+ tmp_len -= tmp_len;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ memcpy(p, f->outbox, tmp_len);
|
|
|
+ uint8_t *tmp = calloc(1, f->outbox_data_len - tmp_len);
|
|
|
+ f->outbox_data_len -= tmp_len;
|
|
|
+ f->outbox_len -= tmp_len;
|
|
|
+ memcpy(tmp, f->outbox + tmp_len, f->outbox_data_len);
|
|
|
+ free(f->outbox);
|
|
|
+ f->outbox = tmp;
|
|
|
+ tmp_len -= tmp_len;
|
|
|
+ }
|
|
|
} else {
|
|
|
- memcpy(p, f->outbox, tmp_len);
|
|
|
- uint8_t *tmp = calloc(1, f->outbox_len - tmp_len);
|
|
|
- f->outbox_len -= tmp_len;
|
|
|
- memcpy(tmp, f->outbox + tmp_len, f->outbox_len);
|
|
|
- free(f->outbox);
|
|
|
- f->outbox = tmp;
|
|
|
- tmp_len -= tmp_len;
|
|
|
+ //all we have are ingored bytes
|
|
|
+ if(tmp_len >= f->outbox_len){
|
|
|
+ printf("ignoring %d bytes\n", f->outbox_len);
|
|
|
+ p += f->outbox_len;
|
|
|
+ tmp_len -= f->outbox_len;
|
|
|
+ f->outbox_len = 0;
|
|
|
+ } else {
|
|
|
+ printf("ignoring %d bytes\n", tmp_len);
|
|
|
+ f->outbox_len -= tmp_len;
|
|
|
+ tmp_len -= tmp_len;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
while(tmp_len > 0){
|
|
|
+ printf("remaining packet length: %d\n", tmp_len);
|
|
|
|
|
|
struct record_header *record_hdr = (struct record_header*) p;
|
|
|
uint32_t record_length = RECORD_LEN(record_hdr);
|
|
|
+ printf("record length: %d\n", record_length);
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
fprintf(stdout, "Record:\n");
|
|
@@ -412,6 +862,18 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
|
|
|
#endif
|
|
|
|
|
|
p += RECORD_HEADER_LEN;
|
|
|
+
|
|
|
+ if(record_length > tmp_len){
|
|
|
+ //the packet is smaller than the record, we'll just forfeit the data
|
|
|
+ printf("Cannot see all of record\n");
|
|
|
+ tmp_len -= RECORD_HEADER_LEN;
|
|
|
+ f->outbox_len = record_length - tmp_len;
|
|
|
+ f->outbox_data_len = 0;
|
|
|
+ printf("ignoring %d bytes\n", tmp_len);
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
if(record_hdr->type != 0x17){
|
|
|
//TODO: might need to decrypt and re-encrypt
|
|
|
//printf("received non-application data\n");
|
|
@@ -420,6 +882,45 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ //We are at a record boundary: decrypt record and change "leaves"
|
|
|
+ if(!encrypt(f, p, p, record_length, 1, record_hdr->type, 0)){
|
|
|
+ fprintf(stdout,"decryption failed\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ printf("encryption successful\n");
|
|
|
+
|
|
|
+ //look for content type
|
|
|
+ const char *needle = "Content-Type";
|
|
|
+ const char *type = strstr((const char *) p, needle);
|
|
|
+ if(type == NULL){
|
|
|
+ printf("No content-type header\n");
|
|
|
+ tmp_len -= RECORD_HEADER_LEN + record_length;
|
|
|
+ p += record_length;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ uint8_t *substr = calloc(1, 32);
|
|
|
+ memcpy(substr, type, 32);
|
|
|
+ if(strstr((const char *) substr, "img") == NULL){
|
|
|
+ //we're ignoring this
|
|
|
+ printf("This is not a leaf node\n");
|
|
|
+ tmp_len -= RECORD_HEADER_LEN + record_length;
|
|
|
+ p += record_length;
|
|
|
+ free(substr);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ free(substr);
|
|
|
+ if(type != NULL){
|
|
|
+ while((*type != '\r') && (*type != ';')){
|
|
|
+ printf("%c", *type);
|
|
|
+ type ++;
|
|
|
+ }
|
|
|
+ printf("\n");
|
|
|
+ } else {
|
|
|
+ printf("No content-type header\n");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
uint8_t *new_record = calloc(1, record_length);
|
|
|
memcpy(new_record, p, record_length);
|
|
|
uint8_t *tmp_p = new_record;
|
|
@@ -440,7 +941,7 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
printf("Censored queue is at %p.\n", f->censored_queue);
|
|
|
- printf("This block as %d bytes left\n", block_length - offset);
|
|
|
+ printf("This block has %d bytes left\n", block_length - offset);
|
|
|
printf("We need %d bytes\n", remaining);
|
|
|
#endif
|
|
|
|
|
@@ -479,6 +980,7 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
|
|
|
printf("%02x ", tmp_p[EVP_GCM_TLS_EXPLICIT_IV_LEN+i]);
|
|
|
printf("\n");
|
|
|
#endif
|
|
|
+reencrypt:
|
|
|
|
|
|
//step 3: encrypt new record
|
|
|
int32_t success;
|
|
@@ -491,12 +993,13 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
|
|
|
if(record_length +RECORD_HEADER_LEN > tmp_len){
|
|
|
//We have a partial record
|
|
|
memcpy(p, new_record, tmp_len - RECORD_HEADER_LEN);
|
|
|
- f->outbox_len = record_length - (tmp_len - RECORD_HEADER_LEN);
|
|
|
+ f->outbox_data_len = record_length - (tmp_len - RECORD_HEADER_LEN);
|
|
|
//save left-overs in outbox
|
|
|
- f->outbox = calloc(1, f->outbox_len);
|
|
|
+ f->outbox = calloc(1, f->outbox_data_len);
|
|
|
memcpy(f->outbox, new_record + (tmp_len - RECORD_HEADER_LEN),
|
|
|
- f->outbox_len);
|
|
|
+ f->outbox_data_len);
|
|
|
free(new_record);
|
|
|
+ f->outbox_len = f->outbox_data_len;
|
|
|
} else {
|
|
|
memcpy(p, new_record, record_length);
|
|
|
free(new_record);
|
|
@@ -519,9 +1022,74 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
|
|
|
p += record_length;
|
|
|
|
|
|
}
|
|
|
+ */
|
|
|
//step 4: recompute TCP checksum
|
|
|
- tcp_checksum(info);
|
|
|
+ if(changed){
|
|
|
+ tcp_checksum(info);
|
|
|
+ printf("Computing checksum\n");
|
|
|
+ fflush(stdout);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int fill_with_downstream(flow *f, uint8_t *data, int32_t length){
|
|
|
+
|
|
|
+ uint8_t *p = data;
|
|
|
+ int32_t remaining = length;
|
|
|
+ struct slitheen_header *sl_hdr;
|
|
|
+
|
|
|
+ //Fill as much as we can from the censored_queue
|
|
|
+ while((remaining > SLITHEEN_HEADER_LEN) && downstream_queue->first_block != NULL){
|
|
|
+ queue_block *first_block = downstream_queue->first_block;
|
|
|
+ int32_t block_length = first_block->len;
|
|
|
+ int32_t offset = first_block->offset;
|
|
|
|
|
|
+//#ifdef DEBUG
|
|
|
+ printf("Censored queue is at %p.\n", first_block);
|
|
|
+ printf("This block has %d bytes left\n", block_length - offset);
|
|
|
+ printf("We need %d bytes\n", remaining);
|
|
|
+//#endif
|
|
|
+
|
|
|
+ sl_hdr = (struct slitheen_header *) p;
|
|
|
+ sl_hdr->stream_id = first_block->stream_id;
|
|
|
+ sl_hdr->len = 0x00;
|
|
|
+ sl_hdr->garbage = 0x00;
|
|
|
+ p += SLITHEEN_HEADER_LEN;
|
|
|
+ remaining -= SLITHEEN_HEADER_LEN;
|
|
|
+
|
|
|
+ if(block_length > offset + remaining){
|
|
|
+ //use part of the block, update offset
|
|
|
+ memcpy(p, first_block->data+offset, remaining);
|
|
|
+ first_block->offset += remaining;
|
|
|
+ p += remaining;
|
|
|
+ sl_hdr->len = remaining;
|
|
|
+ remaining -= remaining;
|
|
|
+ } else {
|
|
|
+ //use all of the block and free it
|
|
|
+ memcpy(p, first_block->data+offset, block_length - offset);
|
|
|
+
|
|
|
+ free(first_block->data);
|
|
|
+ downstream_queue->first_block = first_block->next;
|
|
|
+ free(first_block);
|
|
|
+
|
|
|
+ p += (block_length - offset);
|
|
|
+ sl_hdr->len = (block_length - offset);
|
|
|
+ remaining -= (block_length - offset);
|
|
|
+ }
|
|
|
+ sl_hdr->len = htons(sl_hdr->len);
|
|
|
+ }
|
|
|
+ //now, if we need more data, fill with garbage
|
|
|
+ if(remaining > SLITHEEN_HEADER_LEN ){
|
|
|
+ //TODO: note, we may also be receiving misordered packets. Take Ian's suggestion into account here
|
|
|
+ sl_hdr = (struct slitheen_header *) p;
|
|
|
+ sl_hdr->stream_id = 0x00;
|
|
|
+ remaining -= SLITHEEN_HEADER_LEN;
|
|
|
+ sl_hdr->len = remaining;
|
|
|
+ sl_hdr->garbage = remaining;
|
|
|
+ p += SLITHEEN_HEADER_LEN;
|
|
|
+ memset(p, 'A', remaining);
|
|
|
+ }
|
|
|
return 0;
|
|
|
}
|
|
|
|