// A simple https server orginally provided by Ilias #include "common.h" #define KEYFILE "server.pem" #define PASSWORD "password" #define DHFILE "dh1024.pem" #include #define DEBUG #define MIDDLEBOX_PORT 8423 int tcp_listen(int port) { int sock; struct sockaddr_in sin; int val=1; if((sock=socket(AF_INET,SOCK_STREAM,0))<0) err_exit("Couldn't make socket"); memset(&sin,0,sizeof(sin)); sin.sin_addr.s_addr=INADDR_ANY; sin.sin_family=AF_INET; sin.sin_port=htons(port); setsockopt(sock,SOL_SOCKET,SO_REUSEADDR, &val,sizeof(val)); if(bind(sock,(struct sockaddr *)&sin, sizeof(sin))<0) berr_exit("Couldn't bind"); listen(sock,5); #ifdef DEBUG printf("Listening at port: %d\n", port); #endif return(sock); } // TCP connect function int tcp_connect(char *host, int port){ struct hostent *hp; struct sockaddr_in addr; int sock; // Resolve host if(!(hp = gethostbyname(host))){ berr_exit("Couldn't resolve host"); } #ifdef DEBUG printf("Host %s resolved to %s port %d\n", host, hp->h_addr_list[0], port); #endif memset(&addr, 0, sizeof(addr)); addr.sin_addr = *(struct in_addr*) hp->h_addr_list[0]; addr.sin_family = AF_INET; addr.sin_port = htons(port); if((sock=socket(AF_INET,SOCK_STREAM, IPPROTO_TCP))<0){ err_exit("Couldn't create socket"); } #ifdef DEBUG printf("Socket created\n"); #endif if(connect(sock,(struct sockaddr *)&addr, sizeof(addr))<0){ err_exit("Couldn't connect socket"); } #ifdef DEBUG printf("Socket connected\n"); #endif return sock; } void load_dh_params(ctx,file) SSL_CTX *ctx; char *file; { DH *ret=0; BIO *bio; if ((bio=BIO_new_file(file,"r")) == NULL) berr_exit("Couldn't open DH file"); ret=PEM_read_bio_DHparams(bio,NULL,NULL, NULL); BIO_free(bio); if(SSL_CTX_set_tmp_dh(ctx,ret)<0) berr_exit("Couldn't set DH parameters"); } void generate_eph_rsa_key(ctx) SSL_CTX *ctx; { RSA *rsa; rsa=RSA_generate_key(512,RSA_F4,NULL,NULL); if (!SSL_CTX_set_tmp_rsa(ctx,rsa)) berr_exit("Couldn't set RSA key"); RSA_free(rsa); } // Check for SSL_write error (just write at this point) -- TO DO: check behavior per slice void check_SSL_write_error(SSL *ssl, int r, int request_len){ switch(SSL_get_error(ssl, r)){ case SSL_ERROR_NONE: if(request_len != r){ err_exit("Incomplete write!"); } break; default: berr_exit("SSL write problem"); } } // Simple test for SPP static int print_ssl_debug_info(SSL *ssl){ int N_proxies, N_slices, i; // print proxies N_proxies = ssl->proxies_len; for (i = 0; i < N_proxies; i++){ printf("Proxy: %s\n", ssl->proxies[i]->address); } // print slices N_slices = ssl->slices_len; for (i = 0; i < N_slices; i++){ printf("Slice with ID %d and purpose %s\n", ssl->slices[i]->slice_id, ssl->slices[i]->purpose); } // All good return 0; } //this creates a new outgoing SSL connection. If method = SSL it also runs SSL_connect SSL* create_SSL_connection(char *address, char* method){ #ifdef DEBUG printf("Creating new ssl/spp conection to: %s\n", address); #endif SSL_CTX *ctx; // SSL context SSL *new_ssl; // SSL context BIO *sbio; // ? int sock; // socket char* ipv4 = strtok(strdup(address), ":"); // ip int port = atoi(strtok(NULL, ":")); // port #ifdef DEBUG printf("Connecting to next hop: %s %d\n", ipv4, port); #endif ctx = initialize_ctx(KEYFILE, PASSWORD, method); new_ssl = SSL_new(ctx); sock = tcp_connect(ipv4, port); sbio = BIO_new_socket(sock, BIO_NOCLOSE); SSL_set_bio(new_ssl, sbio, sbio); //also run connect for ssl (we do not run this when the callback is used for SPP) if (strcmp(method, "ssl")==0) { #ifdef DEBUG printf("SSL CONNECT\n"); #endif if(SSL_connect(new_ssl) <= 0) berr_exit("SSL connect error"); #ifdef DEBUG printf("SSL CONNECTED\n"); #endif } return new_ssl; } /* TODO: replace the content of this with the call to the function above (create_SSL_connection) */ SSL* SPP_Callback(SSL *ssl, char *address){ #ifdef DEBUG printf("Callback called with address: %s\n", address); #endif SSL_CTX *ctx; // SSL context SSL *new_ssl; // SSL context BIO *sbio; // ? int sock; // socket char* ipv4 = strtok(address, ":"); // ip int port = atoi(strtok(NULL, ":")); // port #ifdef DEBUG printf("Connecting to next hop: %s %d\n", ipv4, port); #endif ctx = initialize_ctx(KEYFILE, PASSWORD, "middlebox"); new_ssl = SSL_new(ctx); sock = tcp_connect(ipv4, port); sbio = BIO_new_socket(sock, BIO_NOCLOSE); SSL_set_bio(new_ssl, sbio, sbio); #ifdef DEBUG printf("Connected to next hop...: %s %d\n", ipv4, port); #endif return new_ssl; } int shut_down_connections(SSL* ssl) { int r; int socket; #ifdef DEBUG printf("[middlebox ] Shutting down connection!\n"); #endif // Shutdown SSL - TO DO (check what happens here) r = SSL_shutdown(ssl); if (r == 0) { shutdown(SSL_get_fd(ssl), SHUT_WR); r = SSL_shutdown(ssl); } // Verify that all went good switch(r){ case 1: printf("Closed connection \n"); break; // Success case 0: case -1: default: // Error printf("[middlebox-p] Shutdown failed\n"); } socket = SSL_get_fd(ssl); close(socket); return 0; } int handle_previous_hop_data(SSL* prev_ssl, SSL* next_ssl, char* proto) { int r,w, status; char buf[BUFSIZZ]; SPP_SLICE *slice; SPP_CTX *ctx; // Read HTTP GET (assuming a single read is enough) while(1){ #ifdef DEBUG printf("[middlebox-p] Waiting to read data from previous hop\n"); #endif if (strcmp(proto, "spp") == 0) { r = SPP_read_record(prev_ssl, buf, BUFSIZZ, &slice, &ctx); } else { r = SSL_read(prev_ssl, buf, BUFSIZZ); } status = SSL_get_error(prev_ssl, r); if (status == SSL_ERROR_ZERO_RETURN || status == SSL_ERROR_SYSCALL ){ #ifdef DEBUG fprintf(stderr, "[middlebox-p] Connection with previous hop closed, exiting next hop thread\n"); #endif break; } else if (status != SSL_ERROR_NONE){ berr_exit("[middlebox-p] SSL read problem"); } #ifdef DEBUG printf("[middlebox-p] Data received (from previous hop) (length %d bytes):\n*****\n", r); fwrite(buf, 1, r, stdout); printf("\n******\n"); printf("[middlebox-p] Forwarding record to next hop\n"); #endif if (strcmp(proto, "spp") == 0) { //w = SPP_write_record(next_ssl, buf, r, next_ssl->slices[0]); w = SPP_forward_record(next_ssl, buf,r , slice, ctx, 0); check_SSL_write_error(next_ssl, w, r); } else { w = SSL_write(next_ssl, buf, r); check_SSL_write_error(next_ssl, w, r); } //Look for the blank line that signals the end of the HTTP header if(r == 0 ){ break; } } //shut_down_connections(prev_ssl); SSL_free(prev_ssl); // All good return(0); } int handle_next_hop_data(SSL* prev_ssl, SSL* next_ssl, char* proto) { int r,w,status ; char buf[BUFSIZZ]; SPP_SLICE *slice; SPP_CTX *ctx; // Read HTTP GET (assuming a single read is enough) while(1){ #ifdef DEBUG printf("[middlebox-n] Waiting to read data from next hop\n"); #endif if (strcmp(proto, "spp") == 0) { r = SPP_read_record(next_ssl, buf, BUFSIZZ, &slice, &ctx); } else { r = SSL_read(next_ssl, buf, BUFSIZZ); } status = SSL_get_error(next_ssl, r); if (status == SSL_ERROR_ZERO_RETURN || status == SSL_ERROR_SYSCALL ){ #ifdef DEBUG fprintf(stderr, "[middlebox-n] Connection with next hop closed, exiting next hop thread\n"); #endif break; } else if (status != SSL_ERROR_NONE){ berr_exit("[middlebox-n] SSL read problem"); } #ifdef DEBUG printf("[middlebox-p] Data received (from previous hop) (length %d bytes):\n*****\n", r); fwrite(buf, 1, r, stdout); printf("\n******\n"); printf("[middlebox-n] Forwarding record to previous hop\n"); #endif if (strcmp(proto, "spp") == 0) { w = SPP_forward_record(prev_ssl, buf,r , slice, ctx, 0); check_SSL_write_error(prev_ssl, w, r); } else { w = SSL_write(prev_ssl, buf, r); check_SSL_write_error(prev_ssl, w, r); } // End of socket.... if(r == 0 ){ break; } } //shut_down_connections(next_ssl); SSL_free(next_ssl); #ifdef DEBUG fprintf(stderr, "[middlebox-n] Triggering connection with previous hop to close too\n"); #endif shut_down_connections(prev_ssl); // All good return(0); } int handle_data(SSL* prev_ssl, SSL* next_ssl, char* proto) { pid_t next_handler; int status; #ifdef DEBUG printf("[middlebox] Initializing data handlers\n"); #endif next_handler = fork(); if( next_handler == 0) { //child process // handle traffic from client handle_previous_hop_data(prev_ssl, next_ssl, proto); #ifdef DEBUG printf("[middlebox] Exiting previous hop handler\n"); #endif //TODO: wait for child before returning exit(0); } else { //parent handle_next_hop_data(prev_ssl, next_ssl, proto); #ifdef DEBUG printf("[middlebox] Exiting next hop handler\n"); #endif } #ifdef DEBUG printf("[middlebox] Waiting previous hop handler to quit before quiting data handler\n"); #endif wait(&status); #ifdef DEBUG printf("[middlebox] Exiting data handler!\n"); #endif return 0; } // Usage function void usage(void){ printf("usage: wclient -c -a \n"); printf("-c: protocol chosen (ssl ; spp)\n"); printf("-a: {for ssl splitting only: address to forward in ip:port format}\n"); printf("-p: {port number that the box will listen at (default 8423)}\n"); exit(-1); } // Main function int main(int argc, char **argv){ int sock, s; BIO *sbio; SSL_CTX *ctx; SSL *ssl; int r; pid_t pid; char *proto; // protocol type int port = 8423; extern char *optarg; // user input parameters char *address_to_forward = NULL; int c; proto = "spp"; // Handle user input parameters while((c = getopt(argc, argv, "c:a:p:")) != -1){ switch(c){ // Protocol chosen case 'c': if(! (proto = strdup(optarg) )) err_exit("Out of memory"); break; // File requested for HTTP GET case 'a': if(! (address_to_forward = strdup(optarg) )) err_exit("Out of memory"); break; // Client/Server behavior case 'p': if(! (port = atoi(optarg) )) err_exit("A port NUMBER for the middlebox should be given\n"); break; // default case default: usage(); break; } } if ((strcmp(proto, "spp") != 0) && (strcmp(proto, "ssl") != 0)) { printf("Protocol type specified is not supported. Supported protocols are: spp, ssl\n"); usage(); } else if ( strcmp(proto, "ssl") == 0 && address_to_forward == NULL ) { printf("You must specify a forwarding address for SSL splitting (-a)\n"); usage(); } //initialize SSL connection if (strcmp(proto, "spp") == 0) ctx = initialize_ctx(KEYFILE, PASSWORD, "middlebox"); else ctx = initialize_ctx(KEYFILE, PASSWORD, "ssl"); load_dh_params(ctx,DHFILE); // Socket in listen state sock = tcp_listen(port); while(1){ if((s = accept(sock, 0, 0)) < 0){ err_exit("Problem socket accept\n"); } // fork a new proces if((pid = fork())){ close(s); } else { sbio = BIO_new_socket(s, BIO_NOCLOSE); ssl = SSL_new(ctx); SSL_set_bio(ssl, sbio, sbio); SSL** ssl_next = NULL; if (strcmp(proto, "spp") == 0) { SSL* (*connect_func)(SSL *, char *) = SPP_Callback; char *prxy_address = strdup("127.0.0.1:8423"); if ((r = SPP_proxy(ssl, prxy_address, connect_func, ssl_next)) <= 0) { berr_exit("[middlebox] SPP proxy error"); } else { #ifdef DEBUG printf("[middlebox] SPP proxy OK\n"); #endif } } else if (strcmp(proto, "ssl") == 0) { if((r = SSL_accept(ssl) <= 0)){ berr_exit("SSL accept error"); } else { #ifdef DEBUG printf("SSL accept OK\n"); #endif } SSL* new_connection = create_SSL_connection(address_to_forward, "ssl"); ssl_next = &new_connection; } handle_data(ssl, *ssl_next, proto); // Close socket and exit child thread close(s); } } // Clean context destroy_ctx(ctx); // Exit exit(0); }