#include #include #include #include #include #include "flow.h" #include "crypto.h" #include "slitheen.h" static flow_table *table; /* Initialize the table of tagged flows */ int init_flow_table(void) { table = malloc(sizeof(flow_table)); table->table = (flow *) malloc(sizeof(flow)*MAX_FLOWS); if( table->table == NULL){ fprintf(stderr, "malloc failed.\n"); return 1; } table->len = 0; table->max_len = MAX_FLOWS; return 0; } /* Add a new flow to the tagged flow table */ flow *add_flow(flow newFlow) { flow *ptr; if(table->len == table->max_len){ //grow_table(); NULL; } printf("there are %d flows in the table\n", table->len); ptr = table->table + table->len; newFlow.state = TLS_CLNT_HELLO; newFlow.in_encrypted = 0; newFlow.out_encrypted = 0; newFlow.application = 0; newFlow.packet_chain = NULL; newFlow.censored_queue = NULL; newFlow.outbox_len = 0; newFlow.finish_md_ctx = EVP_MD_CTX_create(); const EVP_MD *md = EVP_sha384(); EVP_DigestInit_ex(newFlow.finish_md_ctx, md, NULL); memset(newFlow.read_seq, 0, 8); memset(newFlow.write_seq, 0, 8); *ptr = newFlow; table->len ++; return ptr; } /* Updates the flow state */ int update_flow(flow *f) { uint8_t *record; const struct record_header *record_hdr; const struct handshake_header *handshake_hdr; uint8_t *p = f->packet_chain->data; record_hdr = (struct record_header*) p; int record_len; int data_len; record_len = RECORD_LEN(record_hdr)+RECORD_HEADER_LEN; data_len = f->packet_chain->data_len; packet *current = f->packet_chain; int incoming = current->incoming; record = calloc(1, record_len); for(int i=0; (i data_len) { if(current->next == NULL){ free(record); return 0; } if(current->next->seq_num != current->seq_num + current->len){ printf("Missing packet: seq_num= %d, datalen= %d, nextseq= %d\n", current->seq_num, current->len, current->next->seq_num); free(record); return 0; } current = current->next; p = current->data; int i; for(i=0; (idata_len) && (i+data_len < record_len); i++){ record[data_len+i] = p[i]; } data_len += current->data_len; } switch(record_hdr->type){ case HS: p = record; p += RECORD_HEADER_LEN; if((incoming && f->in_encrypted) || (!incoming && f->out_encrypted)){ int32_t n = encrypt(f, p, p, record_len - RECORD_HEADER_LEN, incoming, 0x16, 0); p += EVP_GCM_TLS_EXPLICIT_IV_LEN; update_context(f, p, n, incoming, 0x16, 0); if(incoming) f->in_encrypted = 2; else f->out_encrypted = 2; } 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); break; case TLS_SERV_HELLO: extract_server_random(f, p); update_finish_hash(f, p); printf("Received server hello!\n"); break; case TLS_NEW_SESS: update_finish_hash(f, p); printf("Received new session ticket!\n"); break; case TLS_CERT: 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"); } if(compute_master_secret(f)){ printf("Error computing master secret\n"); } break; case TLS_CERT_REQ: update_finish_hash(f, p); printf("Received certificate request!\n"); break; case TLS_SRVR_HELLO_DONE: update_finish_hash(f, p); printf("Received server hello done (seq_num = %d)!\n", current->seq_num); break; case TLS_CERT_VERIFY: update_finish_hash(f, p); printf("Received certificate verify!\n"); break; case TLS_CLNT_KEYEX: 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; if(current->incoming) f->seq_num = current->seq_num + current->len; while(current->next != NULL){ current = current->next; if(current->incoming) f->seq_num = current->seq_num+ current->len; } } break; default: printf("Error? %02x\n",p[0]); break; } break; case APP: printf("Application Data\n"); //decrypt this break; case CCS: printf("Change of Cipher Spec\n"); if(incoming){ f->in_encrypted = 1; } else { f->out_encrypted = 1; } /*Initialize ciphers */ init_ciphers(f); break; case ALERT: p = record; p += RECORD_HEADER_LEN; encrypt(f, p, p, record_len - RECORD_HEADER_LEN, incoming, 0x16, 0); p += EVP_GCM_TLS_EXPLICIT_IV_LEN; printf("Alert: %02x %02x\n", p[0], p[1]); break; case HB: printf("Heartbeat\n"); break; default: printf("Error: Not a Record\n"); //TODO: later figure this out, for now delete f->packet_chain = f->packet_chain->next; if( f->packet_chain != NULL){ update_flow(f); } return 0; } //TODO: clean this up if(!f->application){ f->seq_num = current->seq_num; if(record_len == data_len){ /* record ended on packet boundary */ f->packet_chain = current->next; } else { /* need to update data */ f->packet_chain = current; current->data = current->data + (current->data_len - (data_len - record_len)); current->data_len = data_len - record_len; update_flow(f); } } free(record); return 0; } int remove_flow(int index) { int i; flow *ptr; if(index){ ptr = table->table + index -1; for(i=0; i< table->len - index; i++){ ptr += i; *ptr = *(ptr + 1); } table->len --; } else { return 1; } printf("flow removed!\n"); return 0; } 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. */ int check_flow(flow observed){ /* Loop through flows in table and see if it exists */ int i; flow *candidate = table->table; /* Check first in this direction */ for(i=0; ilen; i++){ candidate += i; if(candidate->src_ip.s_addr == observed.src_ip.s_addr){ if(candidate->dst_ip.s_addr == observed.dst_ip.s_addr){ if(candidate->src_port == observed.src_port){ if(candidate->dst_port == observed.dst_port){ return i+1; } } } } } candidate = table->table; /* Then in the other direction */ for(i=0; ilen; i++){ candidate += i; if(candidate->src_ip.s_addr == observed.dst_ip.s_addr){ if(candidate->dst_ip.s_addr == observed.src_ip.s_addr){ if(candidate->src_port == observed.dst_port){ if(candidate->dst_port == observed.src_port){ return i+1; } } } } } return 0; } flow *get_flow(int index){ if(index < table->len){ return table->table+index; } else { return NULL; } } /* Adds a packet the flow's packet chain */ int add_packet(flow *f, struct packet_info *info){ packet *new_packet = malloc(sizeof(packet)); if (info->tcp_hdr == NULL){ return 0; } new_packet->seq_num = htonl(info->tcp_hdr->sequence_num); new_packet->len = info->app_data_len; new_packet->data = info->app_data; new_packet->data_len = new_packet->len; new_packet->next = NULL; new_packet->incoming = (info->ip_hdr->src.s_addr == f->src_ip.s_addr) ? 0 : 1; /* Find appropriate place in chain */ if(new_packet->data_len > 0){ packet *previous = NULL; packet *next = f->packet_chain; while(next != NULL && (next->seq_num <= new_packet->seq_num)){ previous = next; next = next->next; } //place packet after current if(previous == NULL){ //goes at the beginning of chain new_packet->next = f->packet_chain; f->packet_chain = new_packet; } else { new_packet->next = next; previous->next = new_packet; } } return 0; }