|
@@ -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));
|