Sfoglia il codice sorgente

implemented session resumption from session ids and SessionTicket extension

cecylia 9 anni fa
parent
commit
5d19a12491
9 ha cambiato i file con 770 aggiunte e 215 eliminazioni
  1. 1 1
      server/Makefile
  2. 218 15
      server/crypto.c
  3. 1 0
      server/crypto.h
  4. 311 20
      server/flow.c
  5. 28 0
      server/flow.h
  6. 164 36
      server/relay.c
  7. 7 0
      server/relay.h
  8. 33 142
      server/slitheen-proxy.c
  9. 7 1
      server/slitheen.h

+ 1 - 1
server/Makefile

@@ -9,7 +9,7 @@ slitheen-proxy.o flow.o rserv.o ptwist168.o util.o crypto.o relay.o:: ptwist.h r
 rserv: rserv.o ptwist168.o
 	gcc -g -o $@ $^ -lssl -lcrypto
 
-slitheen-proxy: slitheen-proxy.o flow.o rserv.o ptwist168.o util.o crypto.o relay.o relay.h crypto.h util.h ptwist.h rserv.h flow.h slitheen.h
+slitheen-proxy: slitheen-proxy.o flow.o rserv.o ptwist168.o crypto.o relay.o relay.h crypto.h ptwist.h rserv.h flow.h slitheen.h
 	gcc -g -o $@ $^ -I/home/slitheen/Documents/include/openssl libssl.a libcrypto.a -lpcap -lpthread -ldl
 
 clean:

+ 218 - 15
server/crypto.c

@@ -4,10 +4,22 @@
 #include <openssl/err.h>
 #include <openssl/rand.h>
 #include <openssl/ssl.h>
+#include "rserv.h"
 #include "crypto.h"
 #include "flow.h"
 #include "slitheen.h"
 
+/** Updates the hash of all TLS handshake messages upon the
+ *  receipt of a new message. This hash is eventually used
+ *  to verify the TLS Finished message
+ *
+ *  Inputs:
+ *  	f: the tagged flow
+ *  	hs: A pointer to the start of the handshake message
+ *
+ *  Output:
+ *  	0 on success, 1 on failure
+ */
 int update_finish_hash(flow *f, uint8_t *hs){
 	//find handshake length
 	const struct handshake_header *hs_hdr;
@@ -17,10 +29,20 @@ int update_finish_hash(flow *f, uint8_t *hs){
 	
 	EVP_DigestUpdate(f->finish_md_ctx, hs, hs_len+4);
 
-	return 1;
-	
+	return 0;
 }
 
+/** Extracts the server parameters from the server key
+ *  exchange message
+ *
+ *  Inputs:
+ *  	f: the tagged flow
+ *  	hs: the beginning of the server key exchange
+ *  		handshake message
+ *
+ *  Output:
+ *  	0 on success, 1 on failure
+ */
 int extract_parameters(flow *f, uint8_t *hs){
 	DH *dh;
 	uint8_t *p;
@@ -81,8 +103,21 @@ int32_t mac(flow *f, uint8_t *input, uint8_t *output, int32_t len, int32_t incom
 
 }*/
 
-/* Encrypt/decrypt message
+/* Encrypt/Decrypt a TLS record
+ *
+ *  Inputs:
+ * 		f: the tagged flow
+ * 		input: a pointer to the data that is to be encrypted/
+ * 			   decrypted
+ * 		output: a pointer to where the data should be written
+ * 				after it is encrypted or decrypted
+ * 		len: the length of the data
+ * 		incoming: the direction of the record
+ * 		type: the type of the TLS record
+ * 		enc: 1 for encryption, 0 for decryption
  *
+ * 	Output:
+ * 		length of the output data
  */
 int encrypt(flow *f, uint8_t *input, uint8_t *output, int32_t len, int32_t incoming, int32_t type, int32_t enc){
 	uint8_t *p = input;
@@ -141,6 +176,16 @@ int encrypt(flow *f, uint8_t *input, uint8_t *output, int32_t len, int32_t incom
 	return n;
 }
 
+/** Verifies the hash in a TLS finished message
+ *
+ * 	Inputs:
+ * 		f: the tagged flow
+ * 		p: a pointer to the TLS Finished handshake message
+ * 		incoming: the direction of the flow
+ *
+ * 	Output:
+ * 		0 on success, 1 on failure
+ */
 int verify_finish_hash(flow *f, uint8_t *p, int32_t incoming){
 	EVP_MD_CTX ctx;
 	uint8_t hash[EVP_MAX_MD_SIZE];
@@ -168,15 +213,25 @@ int verify_finish_hash(flow *f, uint8_t *p, int32_t incoming){
 
 	//now compare
 	if(CRYPTO_memcmp(p, output, fin_length) != 0){
-		printf("VERIFY FAILED\n");
-		return 0;
+	//	printf("VERIFY FAILED\n");
+		return 1;
 	} else {
 		printf("VERIFY PASSED\n");
 	}
 
-	return 1;
+	return 0;
 }
 
+/** Computes the TLS master secret from the decoy server's
+ *  public key parameters and the leaked secret from the
+ *  extracted Slitheen tag
+ *
+ *  Input:
+ *  	f: the tagged flow
+ *
+ *  Output:
+ *  	0 on success, 1 on failure
+ */
 int compute_master_secret(flow *f){
 	DH *dh_srvr = NULL;
 	DH *dh_clnt = NULL;
@@ -238,21 +293,38 @@ int compute_master_secret(flow *f){
 	
 	PRF(pre_master_secret, PRE_MASTER_LEN, (uint8_t *) TLS_MD_MASTER_SECRET_CONST, TLS_MD_MASTER_SECRET_CONST_SIZE, f->client_random, SSL3_RANDOM_SIZE, f->server_random, SSL3_RANDOM_SIZE, NULL, 0, f->master_secret, SSL3_MASTER_SECRET_SIZE);
 
+	if(f->current_session != NULL)
+		memcpy(f->current_session, f->master_secret, SSL3_MASTER_SECRET_SIZE);
+
+#ifdef DEBUG
+    fprintf(stderr, "Premaster Secret:\n");
+    BIO_dump_fp(stderr, (char *)pre_master_secret, PRE_MASTER_LEN);
+    fprintf(stderr, "Client Random:\n");
+    BIO_dump_fp(stderr, (char *)f->client_random, SSL3_RANDOM_SIZE);
+    fprintf(stderr, "Server Random:\n");
+    BIO_dump_fp(stderr, (char *)f->server_random, SSL3_RANDOM_SIZE);
+    fprintf(stderr, "Master Secret:\n");
+    BIO_dump_fp(stderr, (char *)f->master_secret, SSL3_MASTER_SECRET_SIZE);
+#endif
+
 	//remove pre_master_secret from memory
 	memset(pre_master_secret, 0, PRE_MASTER_LEN);
 
-	/*printf("master secret:\n");
-	for(int i=0; i< 48; i++){
-		printf("%02x ", f->master_secret[i]);
-	}
-	printf("\n");*/
-	
 	free(pre_master_secret);
 	DH_free(dh_srvr);
 	DH_free(dh_clnt);
 	return 0;
 }
 
+/** Saves the random none from the server hello message
+ *
+ *  Inputs:
+ *  	f: the tagged flow
+ *  	hs: a pointer to the beginning of the server hello msg
+ *  
+ *  Output:
+ *  	none
+ */
 void extract_server_random(flow *f, uint8_t *hs){
 
 	uint8_t *p;
@@ -265,7 +337,20 @@ void extract_server_random(flow *f, uint8_t *hs){
 
 }
 
-/* PRF using sha384, as defined in RFC 5246 */
+/** PRF using sha384, as defined in RFC 5246
+ *  
+ *  Inputs:
+ *  	secret: the master secret used to sign the hash
+ *  	secret_len: the length of the master secret
+ *  	seed{1, ..., 4}: seed values that are virtually
+ *  		concatenated
+ *  	seed{1,...4}_len: length of the seeds
+ *  	output: a pointer to the output of the PRF
+ *  	output_len: the number of desired bytes
+ *
+ *  Output:
+ *  	0 on success, 1 on failure
+ */
 int PRF(uint8_t *secret, int32_t secret_len,
 		uint8_t *seed1, int32_t seed1_len,
 		uint8_t *seed2, int32_t seed2_len,
@@ -340,10 +425,17 @@ int PRF(uint8_t *secret, int32_t secret_len,
 			remaining -= remaining;
 		}
 	}
-	return 1;
+	return 0;
 }
 
-/* After receiving change cipher spec, calculate keys from master secret */
+/** After receiving change cipher spec, calculate keys from master secret
+ *  
+ *  Input:
+ *  	f: the tagged flow
+ *
+ *  Output:
+ *  	0 on success, 1 on failure
+ */
 int init_ciphers(flow *f){
 
 	EVP_CIPHER_CTX *r_ctx;
@@ -372,6 +464,24 @@ int init_ciphers(flow *f){
 			key_block, total_len);
 
 #ifdef DEBUG
+	printf("master secret:\n");
+	for(int i=0; i< SSL3_MASTER_SECRET_SIZE; i++){
+		printf("%02x ", f->master_secret[i]);
+	}
+	printf("\n");
+
+	printf("client random:\n");
+	for(int i=0; i< SSL3_RANDOM_SIZE; i++){
+		printf("%02x ", f->client_random[i]);
+	}
+	printf("\n");
+
+	printf("server random:\n");
+	for(int i=0; i< SSL3_RANDOM_SIZE; i++){
+		printf("%02x ", f->server_random[i]);
+	}
+	printf("\n");
+
 	printf("keyblock:\n");
 	for(int i=0; i< total_len; i++){
 		printf("%02x ", key_block[i]);
@@ -476,3 +586,96 @@ void update_context(flow *f, uint8_t *input, int32_t len, int32_t incoming, int3
 
 	free(output);
 }
+
+/** Checks a handshake message to see if it is tagged or a
+ *  recognized flow. If the client random nonce is tagged,
+ *  adds the flow to the flow table to be tracked.
+ *
+ *  Inputs:
+ *  	info: the processed packet
+ *  	f: the tagged flow
+ *
+ *  Output:
+ *  	none
+ */
+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", "ab");
+			if (fp == NULL) {
+				perror("fopen");
+				exit(1);
+			}
+			//Write tag to file
+			for(i=0; i< 28; i++){
+				fprintf(fp, "%02x ", p[i]);
+			}
+			fprintf(fp, "\n");
+			fclose(fp);
+
+			//Write key to file
+			fp = fopen("sharedkey", "ab");
+			if (fp == NULL) {
+				perror("fopen");
+				exit(1);
+			}
+			for(i=0; i<16;i++){
+			    fprintf(fp, "%02x", key[i]);
+			}
+			fprintf(fp, "\n");
+			fclose(fp);
+			printf("Received tagged flow! (key =");
+			for(i=0; i<16;i++){
+			    printf(" %02x", key[i]);
+			}
+			printf(")\n");
+
+			/* 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");
+
+		}
+	}
+
+}

+ 1 - 0
server/crypto.h

@@ -21,6 +21,7 @@ int update_finish_hash(flow *f, uint8_t *hs);
 int verify_finish_hash(flow *f, uint8_t *p, int32_t incoming);
 int init_ciphers(flow *f);
 void update_context(flow *f, uint8_t *input, int32_t len, int32_t incoming, int32_t type, int32_t enc);
+void check_handshake(struct packet_info *info, flow f);
 
 #define PRE_MASTER_LEN 256
 

+ 311 - 20
server/flow.c

@@ -8,6 +8,7 @@
 #include "slitheen.h"
 
 static flow_table *table;
+static session_cache *sessions;
 
 /* Initialize the table of tagged flows */
 int init_flow_table(void) {
@@ -41,6 +42,8 @@ flow *add_flow(flow newFlow) {
 	newFlow.in_encrypted = 0;
 	newFlow.out_encrypted = 0;
 	newFlow.application = 0;
+	newFlow.resume_session = 0;
+	newFlow.current_session = NULL;
 	newFlow.packet_chain = NULL;
 	newFlow.censored_queue = NULL;
 	newFlow.outbox_len = 0;
@@ -58,7 +61,15 @@ flow *add_flow(flow newFlow) {
 	return ptr;
 }
 
-/* Updates the flow state */
+/** Observes TLS handshake messages and updates the state of
+ *  the flow
+ *
+ *  Inputs:
+ *  	f: the tagged flow
+ *
+ *  Output:
+ *  	0 on success, 1 on failure
+ */
 int update_flow(flow *f) {
 	uint8_t *record;
 	const struct record_header *record_hdr;
@@ -116,33 +127,34 @@ int update_flow(flow *f) {
 			handshake_hdr = (struct handshake_header*) p;
 			f->state = handshake_hdr->type;
 
-			printf("record length: %d, hanshake length: %d\n", record_len, HANDSHAKE_MESSAGE_LEN(handshake_hdr));
-
-			/* Now see if there's anything extra to do */
 			switch(f->state){
-			/* Checks to see if this is a possibly tagged hello msg */
 				case TLS_CLNT_HELLO: 
-					/* Expecting server hello msg */
-					printf("Received client hello!\n");
 					update_finish_hash(f, p);
+					check_session(f, p, HANDSHAKE_MESSAGE_LEN(handshake_hdr));
+					printf("Shaking hands...\n");
 					break;
 				case TLS_SERV_HELLO:
+					printf("Received server hello\n");
+					if(f->resume_session){
+						verify_session_id(f,p);
+					} else {
+						save_session_id(f,p);
+					}
 					extract_server_random(f, p);
 					update_finish_hash(f, p);
-					printf("Received server hello!\n");
 					break;
 				case TLS_NEW_SESS:
+					printf("Received new session\n");
+					save_session_ticket(f, p, HANDSHAKE_MESSAGE_LEN(handshake_hdr));
 					update_finish_hash(f, p);
-					printf("Received new session ticket!\n");
 					break;
 				case TLS_CERT:
+					printf("Received cert\n");
 					update_finish_hash(f, p);
-					printf("Received certificate!\n");
 					break;
 				case TLS_SRVR_KEYEX:
 					update_finish_hash(f, p);
-					printf("Received server key exchange (seq num= %d!\n", current->seq_num);
-					/* Need to extract server params */
+
 					if(extract_parameters(f, p)){
 						printf("Error extracting params\n");
 					}
@@ -152,24 +164,22 @@ int update_flow(flow *f) {
 					break;
 				case TLS_CERT_REQ:
 					update_finish_hash(f, p);
-					printf("Received certificate request!\n");
 					break;
 				case TLS_SRVR_HELLO_DONE:
+					printf("Received server hello done\n");
 					update_finish_hash(f, p);
-					printf("Received server hello done (seq_num = %d)!\n", current->seq_num);
 					break;
 				case TLS_CERT_VERIFY:
+					printf("received cert verify\n");
 					update_finish_hash(f, p);
-					printf("Received certificate verify!\n");
 					break;
 				case TLS_CLNT_KEYEX:
+					printf("Received client key exchange\n");
 					update_finish_hash(f, p);
-					printf("Received client key exchange!\n");
 					break;
 				case TLS_FINISHED:
 					verify_finish_hash(f,p, incoming);
 					update_finish_hash(f, p);
-					printf("Received finished message!\n");
 					if((f->in_encrypted == 2) && (f->out_encrypted == 2)){
 						printf("Handshake complete!\n");
 						f->application = 1;
@@ -189,10 +199,9 @@ int update_flow(flow *f) {
 			break;
 		case APP:
 			printf("Application Data\n");
-			//decrypt this
 			break;
 		case CCS:
-			printf("Change of Cipher Spec\n");
+			printf("CCS\n");
 			if(incoming){
 				f->in_encrypted = 1;
 			} else {
@@ -223,7 +232,7 @@ int update_flow(flow *f) {
 			}
 			return 0;
 	}
-	//TODO: clean this up
+
 	if(!f->application){
 	f->seq_num = current->seq_num;
 
@@ -243,6 +252,15 @@ int update_flow(flow *f) {
 	return 0;
 }
 
+/** Removes the tagged flow from the flow table: happens when
+ *  the station receives a TCP RST or FIN packet
+ *
+ *  Input:
+ *  	index: the index into the flow table of the tagged flow
+ *
+ *  Output:
+ *  	0 on success, 1 on failure
+ */
 int remove_flow(int index) {
 	int i;
 	flow *ptr;
@@ -262,12 +280,21 @@ int remove_flow(int index) {
 	return 0;
 }
 
+/** Expands the flow table when we run out of space
+ *  TODO: implement and test
+ */
 int grow_table() {
 	return 0;
 }
 
 /** Returns the index of a flow in the flow table if
  *  it exists, returns 0 if it is not present.
+ *
+ *  Inputs:
+ *  	observed: details for the observed flow
+ *
+ *  Output:
+ *  	index of flow in table or 0 if it doesn't exist
  */
 int check_flow(flow observed){
 	/* Loop through flows in table and see if it exists */
@@ -307,6 +334,14 @@ int check_flow(flow observed){
 	return 0;
 }
 
+/** Returns the flow in the flow table at a specified index
+ *
+ *  Input:
+ *  	index: the desired index of the flow table
+ *
+ *  Output:
+ *  	the flow at the specified index
+ */
 flow *get_flow(int index){
 	if(index < table->len){
 		return table->table+index;
@@ -315,6 +350,262 @@ flow *get_flow(int index){
 	}
 }
 
+int init_session_cache(void){
+	sessions = malloc(sizeof(session_cache));
+	sessions->length = 0;
+	sessions->first_session = NULL;
+
+	return 0;
+}
+
+/** Called from ServerHello, verifies that the session id returned matches
+ *  the session id requested from the client hello
+ *
+ *  Input:
+ *  	f: the tagged flow
+ *  	hs: a pointer to the ServerHello message
+ *
+ *  Output:
+ *  	0 if success, 1 if failed
+ */
+int verify_session_id(flow *f, uint8_t *hs){
+	
+	//increment pointer to point to sessionid
+	uint8_t *p = hs + HANDSHAKE_HEADER_LEN;
+	p += 2; //skip version
+	p += SSL3_RANDOM_SIZE; //skip random
+
+	uint8_t id_len = (uint8_t) p[0];
+	p ++;
+	
+	//check to see if it matches flow's session id set by ClientHello
+	if(f->current_session != NULL && f->current_session->session_id_len > 0 && !memcmp(f->current_session->session_id, p, id_len)){
+		//if it matched, update flow with master secret :D
+		session *last = sessions->first_session;
+		int found = 0;
+		for(int i=0; ((i<sessions->length) && (!found)); i++){
+			if(!memcmp(last->session_id, f->current_session->session_id, id_len)){
+				memcpy(f->master_secret, last->master_secret, SSL3_MASTER_SECRET_SIZE);
+				found = 1;
+			}
+			last = last->next;
+		}
+		if((!found) && (f->current_session->session_ticket_len > 0)){
+			last = sessions->first_session;
+			for(int i=0; ((i<sessions->length) && (!found)); i++){
+				if(!memcmp(last->session_ticket, f->current_session->session_ticket, f->current_session->session_ticket_len)){
+					memcpy(f->master_secret, last->master_secret, SSL3_MASTER_SECRET_SIZE);
+					found = 1;
+				}
+				last = last->next;
+			}
+		}
+
+	} else if (f->current_session != NULL &&  f->current_session->session_id_len > 0){
+		//check to see if server's hello extension matches the ticket
+		save_session_id(f, p);
+	}
+
+	//now check 
+
+	return 0;
+
+}
+
+/* Called from ClientHello. Checks to see if the session id len is > 0. If so,
+ * saves sessionid for later verification. Also checks to see if a session
+ * ticket is included as an extension.
+ *
+ *  Input:
+ *  	f: the tagged flow
+ *  	hs: a pointer to the ServerHello message
+ *
+ *  Output:
+ *  	0 if success, 1 if failed
+ */
+int check_session(flow *f, uint8_t *hs, uint32_t len){
+
+	uint8_t *p = hs + HANDSHAKE_HEADER_LEN;
+	p += 2; //skip version
+	p += SSL3_RANDOM_SIZE; //skip random
+
+	session *new_session = calloc(1, sizeof(session));
+	new_session->session_id_len = (uint8_t) p[0];
+	new_session->session_ticket_len = 0;
+	p  ++;
+
+	if(new_session->session_id_len > 0){
+		f->resume_session = 1;
+		memcpy(new_session->session_id, p, new_session->session_id_len);
+		new_session->next = NULL;
+
+		f->current_session = new_session;
+	}
+
+	p += new_session->session_id_len;
+	
+	//check to see if there is a session ticket included
+
+	//skip to extensions
+	uint16_t ciphersuite_len = (p[0] << 8) + p[1];
+	p += 2 + ciphersuite_len;
+	uint8_t compress_meth_len = p[0];
+	p += 1 + compress_meth_len;
+	
+	//search for SessionTicket TLS extension
+	if(2 + SSL3_RANDOM_SIZE + new_session->session_id_len + 1 + 2 + ciphersuite_len + 1 + compress_meth_len > len){
+		//no extension
+		if(f->current_session == NULL)
+			free(new_session);
+		return 0;
+	}
+	uint16_t extensions_len = (p[0] << 8) + p[1];
+	p += 2;
+	while(extensions_len > 0){
+		uint16_t type = (p[0] << 8) + p[1];
+		p += 2;
+		uint16_t ext_len = (p[0] << 8) + p[1];
+		p += 2;
+		if(type == 0x23){
+			if(ext_len > 0){
+				f->resume_session = 1;
+				new_session->session_ticket_len = ext_len;
+				new_session->session_ticket = calloc(1, ext_len);
+				memcpy(new_session->session_ticket, p, ext_len);
+				f->current_session = new_session;
+				
+			}
+		}
+		p += ext_len;
+		extensions_len -= (4 + ext_len);
+	}
+
+	if(!f->resume_session){
+		//see if a ticket is incuded
+		free(new_session);
+	}
+
+	return 0;
+}
+	
+
+/* Called from ServerHello during full handshake. Adds the session id to the
+ * cache for later resumptions
+ *
+ *  Input:
+ *  	f: the tagged flow
+ *  	hs: a pointer to the ServerHello message
+ *
+ *  Output:
+ *  	0 if success, 1 if failed
+ */
+int save_session_id(flow *f, uint8_t *hs){
+	printf("saving session id\n");
+
+	//increment pointer to point to sessionid
+	uint8_t *p = hs + HANDSHAKE_HEADER_LEN;
+	p += 2; //skip version
+	p += SSL3_RANDOM_SIZE; //skip random
+	
+	session *new_session = calloc(1, sizeof(session));
+	new_session->session_id_len = (uint8_t) p[0];
+	if(new_session->session_id_len <= 0){
+		//if this value is zero, the session is non-resumable or the
+		//server will issue a NewSessionTicket handshake message
+		printf("USING TLS SESSION TICKET\n");
+		free(new_session);
+		return 0;
+	}
+	p++;
+	memcpy(new_session->session_id, p, new_session->session_id_len);
+	new_session->next = NULL;
+
+	f->current_session = new_session;
+
+	if(sessions->first_session == NULL){
+		sessions->first_session = new_session;
+	} else {
+		session *last = sessions->first_session;
+
+		for(int i=0; i< sessions->length; i++){
+			if(last == NULL)
+				printf("UH OH: last is null?\n");
+			last = last->next;
+		}
+		last->next = new_session;
+	}
+
+	sessions->length ++;
+
+	printf("Saved session id:");
+	for(int i=0; i< new_session->session_id_len; i++){
+		printf(" %02x", p[i]);
+	}
+	printf("\n");
+
+	printf("THERE ARE NOW %d saved sessions\n", sessions->length);
+
+	return 0;
+
+}
+
+/* Called from NewSessionTicket. Adds the session ticket to the
+ * cache for later resumptions
+ *
+ *  Input:
+ *  	f: the tagged flow
+ *  	hs: a pointer to the ServerHello message
+ *
+ *  Output:
+ *  	0 if success, 1 if failed
+ */
+int save_session_ticket(flow *f, uint8_t *hs, uint32_t len){
+	uint8_t *p = hs + HANDSHAKE_HEADER_LEN;
+	p += 4; //skip lifetime TODO: add to session struct
+	session *new_session = calloc(1,sizeof(session));
+	new_session->session_id_len = 0;
+	
+	new_session->session_ticket_len = (p[0] << 8) + p[1];
+	printf("saving ticket of size %d\n", new_session->session_ticket_len);
+	p += 2;
+
+	uint8_t *ticket = calloc(1, new_session->session_ticket_len);
+	memcpy(ticket, p, new_session->session_ticket_len);
+	new_session->session_ticket = ticket;
+	memcpy(new_session->master_secret, f->master_secret, SSL3_MASTER_SECRET_SIZE);
+
+	if(sessions->first_session == NULL){
+		sessions->first_session = new_session;
+	} else {
+		session *last = sessions->first_session;
+
+		for(int i=0; i< sessions->length; i++){
+			if(last == NULL)
+				printf("UH OH: last is null?\n");
+			last = last->next;
+		}
+		last->next = new_session;
+	}
+
+	sessions->length ++;
+
+	printf("Saved session ticket:");
+	for(int i=0; i< new_session->session_ticket_len; i++){
+		printf(" %02x", p[i]);
+	}
+	printf("\n");
+
+	printf("Saved session master secret:");
+	for(int i=0; i< SSL3_MASTER_SECRET_SIZE; i++){
+		printf(" %02x", new_session->master_secret[i]);
+	}
+	printf("\n");
+
+	printf("THERE ARE NOW %d saved sessions\n", sessions->length);
+
+	return 0;
+}
+
 /* Adds a packet the flow's packet chain */
 int add_packet(flow *f, struct packet_info *info){
 	packet *new_packet = malloc(sizeof(packet));

+ 28 - 0
server/flow.h

@@ -39,6 +39,22 @@ typedef struct queue_block_st{
 
 typedef struct packet_st packet;
 
+typedef struct session_st {
+	uint8_t session_id_len;
+	uint8_t session_id[SSL_MAX_SSL_SESSION_ID_LENGTH];
+	struct session_st *next;
+	uint8_t master_secret[SSL3_MASTER_SECRET_SIZE];
+	uint8_t client_random[SSL3_RANDOM_SIZE];
+	uint8_t server_random[SSL3_RANDOM_SIZE];
+	uint32_t session_ticket_len;
+	uint8_t *session_ticket;
+} session;
+
+typedef struct session_cache_st {
+	session *first_session;
+	uint32_t length;
+} session_cache;
+
 typedef struct flow_st {
 	struct in_addr src_ip, dst_ip; /* Source (client) and Destination (server) addresses */
 	uint16_t src_port, dst_port;	/* Source and Destination ports */
@@ -50,6 +66,7 @@ typedef struct flow_st {
 	int in_encrypted;		/* indicates whether incoming flow is encrypted */
 	int out_encrypted;		/* indicates whether outgoing flow is encrypted */
 	int application; /* indicates handshake is complete */
+	int resume_session;
 	packet *packet_chain; /* currently held data */
 	queue_block *censored_queue;
 	DH *dh;
@@ -67,12 +84,17 @@ typedef struct flow_st {
 	uint8_t server_random[SSL3_RANDOM_SIZE];
 	uint8_t master_secret[SSL3_MASTER_SECRET_SIZE];
 
+	session *current_session;
+
 	uint8_t read_seq[8];
 	uint8_t write_seq[8];
 
 	uint8_t *outbox;
 	int32_t outbox_len;
 
+	//locking
+	//pthread_mutex_t flow_lock = PTHREAD_MUTEX_INITIALIZER;
+
 } flow;
 
 typedef struct flow_table_st {
@@ -89,6 +111,12 @@ int remove_flow(int index);
 int check_flow(flow observed);
 flow *get_flow(int index);
 
+int init_session_cache (void);
+int verify_session_id(flow *f, uint8_t *hs);
+int check_session(flow *f, uint8_t *hs, uint32_t len);
+int save_session_id(flow *f, uint8_t *hs);
+int save_session_ticket(flow *f, uint8_t *hs, uint32_t len);
+
 int add_packet(flow *f, struct packet_info *info);
 
 #endif /* __RELAY_H__ */

+ 164 - 36
server/relay.c

@@ -7,12 +7,24 @@
 #include <netinet/in.h>
 #include <netdb.h>
 #include <unistd.h>
+#include <pthread.h>
 #include "relay.h"
 #include "slitheen.h"
 #include "flow.h"
 #include "crypto.h"
 
-
+/** Called when a TLS application record is received for a
+ *  tagged flow. Upstream packets will be checked for covert
+ *  requests to censored sites, downstream packets will be
+ *  replaced with data from the censored queue or with garbage
+ *
+ *  Inputs:
+ *  	f: the tagged flow
+ *  	info: the processed received application packet
+ *
+ *  Output:
+ *  	0 on success, 1 on failure
+ */
 int replace_packet(flow *f, struct packet_info *info){
 
 	if (info->tcp_hdr == NULL){
@@ -66,6 +78,18 @@ 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
+ *  request to the indicated site and saves the response to
+ *  the censored queue
+ *
+ *  Inputs:
+ *  	f: the tagged flow
+ *  	info: the processed received packet
+ *
+ *  Ouput:
+ *  	0 on success, 1 on failure
+ */
 int read_header(flow *f, struct packet_info *info){
 	uint8_t *p = info->app_data;
 
@@ -95,6 +119,8 @@ int read_header(flow *f, struct packet_info *info){
 		fflush(stdout);
 	}
 
+	//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";
@@ -106,6 +132,7 @@ int read_header(flow *f, struct packet_info *info){
 
 	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;
@@ -146,11 +173,69 @@ int read_header(flow *f, struct packet_info *info){
 		}
 	}
 
+	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
+
+	//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){
+		printf("Failed to create pipe for new thread\n");
+		return 1;
+	}
+
+	struct proxy_thread_data *thread_data = 
+		calloc(1, sizeof(struct proxy_thread_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
+	
+
+	//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,
+			strlen( (const char *) message));
+	if(bytes_written < strlen( (const char *) message)){
+		printf("failed to write all bytes to pipe\n");
+	}
+
+	return 0;
+
+}
+
+/** Called by spawned pthreads in read_header to send upstream
+ *  data to the censored site and receive responses. Downstream
+ *  data is stored in the flow's censored_queue. Function and
+ *  thread will terminate when the client closes the connection
+ *  to the covert destination
+ *
+ *  Input:
+ *  	A struct that contains the following information:
+ *  	- the tagged flow
+ *  	- the name of the server
+ *  	- the read end of the pipe
+ *
+ */
+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("sending request to site: %s\n", server);
+	printf("Collecting data from censored site: %s\n", thread_data->server);
 
 	struct hostent *host;
-	host = gethostbyname((const char *) server);
+	host = gethostbyname((const char *) thread_data->server);
 
 	if(host == NULL){
 		printf("gethostbyname failed\n");
@@ -175,43 +260,84 @@ int read_header(flow *f, struct packet_info *info){
 		printf("error connecting\n");
 		return 0;
 	}
-	int32_t bytes_sent = send(handle, message, strlen((const char *) message), 0);
-	if( bytes_sent < 0){
-		printf("error sending request\n");
-		close(handle);
-		return 0;
-	} else if (bytes_sent < strlen((const char *) message)){
-		close(handle);
-		return 0;
-	}
 
-	
-	int32_t bytes_read;
-	for(int i=0; i<3; i++){
-		uint8_t *buf = calloc(1, BUFSIZ);
-		bytes_read = recv(handle, buf, BUFSIZ, 0);
-		if(bytes_read <= 0) break;
-
-		//make a new queue block
-		queue_block *new_block = calloc(1, sizeof(queue_block));
-		new_block->len = bytes_read;
-		new_block->offset = 0;
-		new_block->data = buf;
-		new_block->next = NULL;
-		if(f->censored_queue == NULL)
-			f->censored_queue = new_block;
-		else{
-			queue_block *last = f->censored_queue;
-			while(last->next != NULL)
-				last = last->next;
-			last->next = new_block;
+	//now select on reading from the pipe and from the socket
+	for(;;){
+		fd_set readfds;
+		fd_set writefds;
+
+		int32_t nfds = (handle > thread_data->pipefd) ?
+			handle +1 : thread_data->pipefd + 1;
+
+		FD_ZERO(&readfds);
+		FD_ZERO(&writefds);
+
+		FD_SET(thread_data->pipefd, &readfds);
+		FD_SET(handle, &readfds);
+		FD_SET(handle, &writefds);
+
+		if (select(nfds, &readfds, &writefds, NULL, NULL) < 0){
+			printf("select error\n");
 		}
-	}
 
-	close(handle);
+		if(FD_ISSET(thread_data->pipefd, &readfds) && FD_ISSET(handle, &writefds)){
+			//we have upstream data ready for writing
+			printf("Passing along upstream data\n");
+
+			int32_t bytes_read = read(thread_data->pipefd, buffer, buffer_len);
+			buffer[buffer_len] = '\0';//TODO: remove w/ print
+			printf("Read from pipe:\n %s\n", buffer);
+
+			if(bytes_read > 0){
+				int32_t bytes_sent = send(handle, buffer,
+						bytes_read, 0);
+				if( bytes_sent < 0){
+					printf("error sending request\n");
+					break;
+				} else if (bytes_sent < bytes_read){
+					//TODO: should update buffer and keep
+					//track of length of upstream data
+					printf("sent less than full upstream bytes\n");
+					break;
+				}
+			}
 
-	return 0;
+		}
+		
+		if (FD_ISSET(handle, &readfds)){
+			//we have downstream data read for saving
+			int32_t bytes_read;
+			uint8_t *buf = calloc(1, BUFSIZ);
+			bytes_read = recv(handle, buf, BUFSIZ, 0);
+			if(bytes_read <= 0){
+				break;
+			}
+			if(bytes_read > 0){
+
+				//make a new queue block
+				queue_block *new_block = calloc(1, sizeof(queue_block));
+				new_block->len = bytes_read;
+				new_block->offset = 0;
+				new_block->data = buf;
+				new_block->next = NULL;
+				if(f->censored_queue == NULL)
+					f->censored_queue = new_block;
+				else{
+					queue_block *last = f->censored_queue;
+					while(last->next != NULL)
+						last = last->next;
+					last->next = new_block;
+				}
+			} else {
+				printf("read 0 bytes\n");
+			}
 
+		}
+	}
+	free(thread_data);
+	free(buffer);
+	close(handle);
+	return 0;
 }
 
 /** Replaces downstream record contents with data from the
@@ -231,6 +357,8 @@ int read_header(flow *f, struct packet_info *info){
  */
 int replace_contents(flow *f, int32_t offset, struct packet_info *info){
 
+	printf("Replacing contents of downstream data\n");
+
 	uint8_t *p = info->app_data;
 
 	int32_t tmp_len = info->app_data_len;
@@ -286,7 +414,7 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
 		p += RECORD_HEADER_LEN;
 		if(record_hdr->type != 0x17){
 			//TODO: might need to decrypt and re-encrypt
-			printf("received non-application data\n");
+			//printf("received non-application data\n");
 			tmp_len -= (record_length+ RECORD_HEADER_LEN);
 			p += record_length;
 			continue;

+ 7 - 0
server/relay.h

@@ -4,9 +4,16 @@
 #include "flow.h"
 #include <stdint.h>
 
+struct proxy_thread_data {
+	flow *f;
+	uint8_t server[50];
+	int32_t pipefd;
+};
+
 int replace_packet(flow *f, struct packet_info *info);
 int replace_contents(flow *f, int32_t offset, struct packet_info *info);
 int read_header(flow *f, struct packet_info *info);
 uint16_t tcp_checksum(struct packet_info *info);
 
+void *proxy_covert_site(void *data);
 #endif /* _RELAY_H_ */

+ 33 - 142
server/slitheen-proxy.c

@@ -5,148 +5,10 @@
 #include <string.h>
 #include <pthread.h>
 #include <openssl/ssl.h>
-#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);
-}
+#include "crypto.h"
 
 void usage(void){
 	printf("Usage: slitheen-proxy [internal network interface] [NAT interface]\n");
@@ -154,8 +16,8 @@ void usage(void){
 
 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 filter1[33] = "ether src host 08:00:27:0e:89:ea";
+	char filter2[33] = "ether src host 08:00:27:0e:89:ea";
 
 	char *dev1 = NULL; /* Device that leads to the internal network */
 	char *dev2 = NULL; /* Device that leads out to the world */
@@ -174,7 +36,7 @@ int main(int argc, char *argv[]){
 	snprintf(filter2, 33, "ether dst host %s", macaddr);
 
 	init_flow_table();
-	init_fragment_table();
+	init_session_cache();
 
 	/* Create threads */
 	outbound.readdev = dev1;
@@ -245,6 +107,35 @@ void *sniff_packets(void *args){
 	return NULL;
 }
 
+void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *packet){
+	pcap_t *handle = (pcap_t *) args;
+
+	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;
+
+	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
+
+}
+
 /* This function receives a full ip packet and then:
  * 	1) identifies the flow
  * 	2) adds the packet to the flow's data chain

+ 7 - 1
server/slitheen.h

@@ -2,8 +2,9 @@
 #define _SLITHEEN_H_
 #include <stdlib.h>
 #include <netinet/in.h>
+#include <pcap.h>
 
-#define macaddr "08:00:27:e8:9d:d4"
+#define macaddr "08:00:27:0e:89:ea"
 
 /* Ethernet addresses are 6 bytes */
 #define ETHER_ADDR_LEN	6
@@ -99,4 +100,9 @@ struct sniff_args {
 	char *filter;
 };
 
+void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *packet);
+void *sniff_packets(void *);
+void process_packet(struct packet_info *info);
+void extract_packet_headers(uint8_t *packet, struct packet_info *info);
+
 #endif /* _SLITHEEN_H_ */