#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.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; //printf("record version(major): %d.\n", (record_hdr->version&0xFF00)>>8); //printf("record version(minor): %d.\n", record_hdr->version&0xFF); //printf("record length: %d.\n", RECORD_LEN(record_hdr)); 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){ //printf("Don't have enought to reconstruct record\n"); 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]; } //printf("Filled %d\n", i); data_len += current->data_len; } switch(record_hdr->type){ case HS: p = record; p += RECORD_HEADER_LEN; //int size_hs = HANDSHAKE_MESSAGE_LEN(handshake_hdr); printf("Handshake Message:\n"); if((incoming && f->in_encrypted) || (!incoming && f->out_encrypted)){ encrypt(f, p, p, record_len - RECORD_HEADER_LEN, incoming, 0x16); p += EVP_GCM_TLS_EXPLICIT_IV_LEN; 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!\n"); /* 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!\n"); 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; } 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); 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; } 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; //TODO: make 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, uint8_t *p){ const struct ip_header *ip_hdr; const struct tcp_header *tcp_hdr; packet *new_packet = malloc(sizeof(packet)); p += ETHER_HEADER_LEN; //skip ethernet header ip_hdr = (struct ip_header*) p; int size_ip = IP_HEADER_LEN(ip_hdr); if (ip_hdr->proto != IPPROTO_TCP){ return 0; } p += size_ip; //skip IP header tcp_hdr = (struct tcp_header*) p; int size_tcp = TCP_HEADER_LEN(tcp_hdr); p += size_tcp; new_packet->seq_num = htonl(tcp_hdr->sequence_num); new_packet->len = htons(ip_hdr->len) - (size_ip + size_tcp); new_packet->data = p; new_packet->data_len = htons(ip_hdr->len) - (size_ip + size_tcp); new_packet->next = NULL; new_packet->incoming = (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)){ //printf("next: %u <= new: %u\n", 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; } /*printf("Flow: %d > %d (%s)\n", ip_hdr->src.s_addr, ip_hdr->dst.s_addr, (new_packet->incoming)? "incoming":"outgoing"); printf("ID number: %u\n", htonl(ip_hdr->id)); printf("Sequence number: %u\n", htonl(tcp_hdr->sequence_num)); printf("Acknowledgement number: %u\n", htonl(tcp_hdr->ack_num)); printf("Length: %d\n", new_packet->data_len);*/ } return 0; }