// Perform a connect void doConnect (char *proto, int slices_len, int N_proxies, SPP_SLICE **slice_set, SPP_PROXY **proxies){ // SPP CONNECT if (strcmp(proto, "spp") == 0){ #ifdef DEBUG printf("[DEBUG] SPP_connect\n"); #endif if (SPP_connect(ssl, slice_set, slices_len, proxies, N_proxies) <= 0){ berr_exit("SPP connect error"); } } // SSL CONNECT if (strcmp(proto, "ssl") == 0){ #ifdef DEBUG printf("[DEBUG] SSL_connect\n"); #endif if(SSL_connect(ssl) <= 0) berr_exit("SSL connect error"); } // TO DO -- Check here if(require_server_auth){ #ifdef DEBUG printf("[DEBUG] Check certificate\n"); #endif check_cert(host); } }
// Load parameters from "dh1024.pem" void load_dh_params(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"); } }
/* Check that the common name matches the host name*/ void check_cert(char *host){ X509 *peer; char peer_CN[256]; long res = SSL_get_verify_result(ssl); if ((res == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) || (res == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)){ #ifdef DEBUG printf("[DEBUG] Self signed certificate accepted\n"); #endif } else { if(res != X509_V_OK) berr_exit("Certificate doesn't verify\n"); } /*Check the cert chain. The chain length is automatically checked by OpenSSL when we set the verify depth in the ctx */ /*Check the common name*/ peer=SSL_get_peer_certificate(ssl); X509_NAME_get_text_by_NID (X509_get_subject_name(peer), NID_commonName, peer_CN, 256); // WARNING NO VALIDATION Validate the hostname /* if (1) { printf("Peer_CN = %s\n", peer_CN); printf("Host = %s\n\n", host); //err_exit("Common name doesn't match host name"); } */ }
static int do_write(int *write_blocked_on_read) { ssize_t w; static unsigned long long bytes=0; if(ratelimit && check_ratelimit(&bytes)) return 0; ERR_clear_error(); w=SSL_write(ssl, writebuf, writebuflen); switch(SSL_get_error(ssl, w)) { case SSL_ERROR_NONE: //logp("wrote: %d\n", w); if(ratelimit) bytes+=w; memmove(writebuf, writebuf+w, writebuflen-w); writebuflen-=w; break; case SSL_ERROR_WANT_WRITE: break; case SSL_ERROR_WANT_READ: *write_blocked_on_read=1; break; case SSL_ERROR_SYSCALL: if(errno == EAGAIN || errno == EINTR) break; logp("Got SSL_ERROR_SYSCALL in write, errno=%d (%s)\n", errno, strerror(errno)); // Fall through to write problem default: berr_exit("SSL write problem"); logp("write returned: %d\n", w); return -1; } return 0; }
// Listen TCP socket int tcp_listen(){ int sock; struct sockaddr_in sin; int val = 1; // Create socket, allocate memory and set sock options 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 (disable_nagle == 1) set_nagle(sock, 1); // Bind to socket if(bind(sock,(struct sockaddr *)&sin, sizeof(sin))<0){ berr_exit("Couldn't bind"); } // Listen to socket listen(sock,5); // Return socket descriptor 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("[DEBUG] Host %s resolved\n", host); #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("[DEBUG] Socket correctly created\n"); #endif if (disable_nagle == 1) set_nagle(sock, 1); if(connect(sock,(struct sockaddr *)&addr, sizeof(addr))<0){ err_exit("Couldn't connect socket"); } #ifdef DEBUG printf("[DEBUG] Socket connected\n"); #endif /* Check solution below if we need to support a timeout -- to debug signal(SIGALRM, AlarmHandler); sTimeout = 0; alarm(CONNECT_TIMEOUT); if ( connect(sock, (struct sockaddr *) &addr, sizeof(addr)) ){ if ( sTimeout ){ err_exit("timeout connecting stream socket"); } } sTimeout = 0; alarm(CONNECT_TIMEOUT); */ return sock; }
SSL_CTX *initialize_ctx(char *keyfile, char *password) { SSL_METHOD *meth; SSL_CTX *ctx; if(!bio_err){ /* Global system initialization*/ SSL_library_init(); SSL_load_error_strings(); /* An error write context */ bio_err=BIO_new_fp(stderr,BIO_NOCLOSE); } /* Set up a SIGPIPE handler */ signal(SIGPIPE,sigpipe_handle); /* Create our context*/ meth=SSLv23_method(); ctx=SSL_CTX_new(meth); /* Load our keys and certificates*/ if(!(SSL_CTX_use_certificate_chain_file(ctx, keyfile))) berr_exit("Can't read certificate file"); pass=password; SSL_CTX_set_default_passwd_cb(ctx, password_cb); if(!(SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))) berr_exit("Can't read key file"); /* Load the CAs we trust*/ if(!(SSL_CTX_load_verify_locations(ctx, CA_FILE, 0))) berr_exit("Can't read CA file"); #if (OPENSSL_VERSION_NUMBER < 0x00905100L) SSL_CTX_set_verify_depth(ctx,1); #endif return ctx; }
// Check for SSL_write error (just write at this point) -- TO DO: check behavior per slice void check_SSL_write_error(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"); } }
/* // Name: init_ctx // In: void // Out: An SSL context // Purpose: Initializes the SSL context by setting up the certificate and // private key. */ SSL_CTX *init_ctx(void) { SSL_METHOD *meth; SSL_CTX *ctx; if(!bio_err) { SSL_library_init(); SSL_load_error_strings(); bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); } meth=SSLv23_method(); ctx=SSL_CTX_new(meth); if(!(SSL_CTX_use_certificate_chain_file(ctx, MYCERT))) { berr_exit("Cant read certificate file"); } if(!(SSL_CTX_use_PrivateKey_file(ctx, KEYFILE, SSL_FILETYPE_PEM))) { berr_exit("Can't read key file"); } if(!SSL_CTX_check_private_key(ctx)) { berr_exit("Key isn't matching"); } return ctx; }
int tcp_listen() { int socket_fd; struct sockaddr_in sin; int val = 1; if((socket_fd = 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(socket_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); if(bind(socket_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) berr_exit("Couldn't bind"); listen(socket_fd, 5); return(socket_fd); }
// Emulate browser behavior based on some input traces static int http_complex(char *proto, char *fn, struct timeval *tvEnd){ int r; char buf[BUFSIZZ]; long bytes_read = 0; // Thread for browser-like behavior pthread_t reading_thread; if(pthread_create(&reading_thread, NULL, browser_replay, fn)) { fprintf(stderr, "Error creating thread\n"); return -1; }else{ #ifdef DEBUG printf("[DEBUG] Reading thread started!\n"); #endif } // Now read the server's response, assuming that it's terminated by a close while(1){ // SPP read if (strcmp(proto, "spp") == 0){ #ifdef DEBUG printf("[DEBUG] Waiting on SPP_read...\n"); #endif SPP_SLICE *slice; // slice for SPP_read SPP_CTX *ctx; // context pointer for SPP_read r = SPP_read_record(ssl, buf, BUFSIZZ, &slice, &ctx); } // SSL read else if (strcmp(proto, "ssl") == 0){ #ifdef DEBUG printf("[DEBUG] Waiting on SSL_read...\n"); #endif r = SSL_read(ssl, buf, BUFSIZZ); } // TCP read else if (strcmp(proto, "pln") == 0){ r = read(plain_socket, buf, BUFSIZZ); experiment_info->app_bytes_read += r; } // Check for errors in read if (strcmp(proto, "spp") == 0 || strcmp(proto, "ssl") == 0){ switch(SSL_get_error(ssl, r)){ case SSL_ERROR_NONE: break; case SSL_ERROR_ZERO_RETURN: berr_exit("SSL error zero return"); case SSL_ERROR_SYSCALL: berr_exit("SSL Error: Premature close"); default: berr_exit("SSL read problem"); } } #ifdef DEBUG printf("Read %d bytes\n", r); #endif // Write buf to stdout #ifdef VERBOSE printf("[DEBUG] Received:\n%s\n\n", buf); #endif // Update counter of bytes read bytes_read += r; //if ( r <= 0 || bytes_read == fSize){ #ifdef DEBUG printf("[DEBUG] File transfer stats %d -- %ld\n", bytes_read, fSize); #endif if (bytes_read == fSize){ #ifdef DEBUG printf("[DEBUG] File transfer done - signaling to other thread\n"); #endif bytes_read = 0; if (running){ thr_exit(); }else{ #ifdef DEBUG printf("[DEBUG] Reading thread is done, so here we are done too\n"); #endif break; } } } // Measure time gettimeofday(tvEnd, NULL); // Shutdown connection #ifdef DEBUG printf("[DEBUG] Shutdown was requested -- HERE\n"); #endif r = SSL_shutdown(ssl); if( !r ){ shutdown(SSL_get_fd(ssl), 1); r = SSL_shutdown(ssl); } switch(r){ case 1: break; // Success case 0: case -1: default: #ifdef DEBUG printf ("Shutdown failed with code %d\n", r); #endif berr_exit("Shutdown failed"); } // Print byte statistics if (stats){ print_stats(ssl); } // Free ssl SSL_free(ssl); // All good return(0); }
static int http_serve(SSL *ssl, int s) { char buf[BUFSIZZ]; int r,len; BIO *io,*ssl_bio; io=BIO_new(BIO_f_buffer()); ssl_bio=BIO_new(BIO_f_ssl()); BIO_set_ssl(ssl_bio,ssl,BIO_CLOSE); BIO_push(io,ssl_bio); r=BIO_gets(io,buf,BUFSIZZ-1); switch(SSL_get_error(ssl,r)){ case SSL_ERROR_NONE: len=r; break; default: berr_exit("SSL read problem"); } char *saveptr; char resource[512] = {'\0'}; char *token = strtok_r(buf, " ", &saveptr); if (token && strcasecmp(token, "GET") == 0) { token = strtok_r(NULL, " ", &saveptr); if (token) { strncpy(resource, token, sizeof(resource)); } } if (resource[0]) { while(1){ r=BIO_gets(io,buf,BUFSIZZ-1); switch(SSL_get_error(ssl,r)){ case SSL_ERROR_NONE: len=r; break; default: berr_exit("SSL read problem"); } /* Look for the blank line that signals the end of the HTTP headers */ if(!strcmp(buf,"\r\n") || !strcmp(buf,"\n")) break; } } if (strcasecmp(resource, "/ciphersuites.txt") == 0) { http_serve_headers(io, 200, "OK", "text/plain"); print_ciphersuite_data(io, ssl, 0); } else if (strcasecmp(resource, "/ciphersuites.js") == 0) { long protocol = SSL_version(ssl); http_serve_headers(io, 200, "OK", "text/javascript"); if(BIO_printf(io, "$(function() {\n insert_text('%s', '", get_protocol_name(protocol)) <= 0) err_exit("Write error"); print_ciphersuite_data(io, ssl, 1); if(BIO_printf(io, "');\n});") <= 0) err_exit("Write error"); } else { http_serve_headers(io, 404, "Not Found", "text/plain"); if(BIO_puts(io, "Not found.") <= 0) err_exit("Write error"); } if((r=BIO_flush(io))<0) err_exit("Error flushing BIO"); r=SSL_shutdown(ssl); if(!r){ /* If we called SSL_shutdown() first then we always get return value of '0'. In this case, try again, but first send a TCP FIN to trigger the other side's close_notify*/ shutdown(s,1); r=SSL_shutdown(ssl); } switch(r){ case 1: break; /* Success */ case 0: case -1: default: berr_exit("Shutdown failed"); } SSL_free(ssl); close(s); return(0); }
void server(int protocol) { int sock,s; BIO *sbio; SSL_CTX *ctx; SSL *ssl; int r; pid_t pid; /* Build our SSL context*/ ctx=initialize_ctx(KEYFILE,PASSWORD); load_dh_params(ctx,DHFILE); SSL_CTX_set_cipher_list(ctx,"ALL"); long options = SSL_OP_NO_TICKET | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; int port; switch (protocol) { case SSL2_VERSION: options |= SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2; port = 4434; break; case SSL3_VERSION: options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2; port = 4435; break; case TLS1_VERSION: options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2; port = 4436; break; case TLS1_1_VERSION: options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_2; port = 4437; break; case TLS1_2_VERSION: options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1; port = 4438; break; default: err_exit("Unexpected protocol value"); } SSL_CTX_set_options(ctx, options); sock=tcp_listen(port); while(1){ if((s=accept(sock,0,0))<0) err_exit("Problem accepting"); if((pid=fork())){ close(s); } else { sbio=BIO_new_socket(s,BIO_NOCLOSE); ssl=SSL_new(ctx); SSL_set_bio(ssl,sbio,sbio); if((r=SSL_accept(ssl)<=0)) berr_exit("SSL accept error"); http_serve(ssl,s); exit(0); } } destroy_ctx(ctx); }
static int http_serve(SSL *ssl, int s) { char buf[BUFSIZZ]; int r,len; BIO *io,*ssl_bio; io=BIO_new(BIO_f_buffer()); ssl_bio=BIO_new(BIO_f_ssl()); BIO_set_ssl(ssl_bio,ssl,BIO_CLOSE); BIO_push(io,ssl_bio); while(1){ r=BIO_gets(io,buf,BUFSIZZ-1); switch(SSL_get_error(ssl,r)){ case SSL_ERROR_NONE: len=r; break; case SSL_ERROR_ZERO_RETURN: goto shutdown; break; default: berr_exit("SSL read problem"); } /* Look for the blank line that signals the end of the HTTP headers */ if(!strcmp(buf,"\r\n") || !strcmp(buf,"\n")) break; } /* Now perform renegotiation if requested */ if(client_auth==CLIENT_AUTH_REHANDSHAKE){ SSL_set_verify(ssl,SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,0); /* Stop the client from just resuming the un-authenticated session */ SSL_set_session_id_context(ssl, (void *)&s_server_auth_session_id_context, sizeof(s_server_auth_session_id_context)); if(SSL_renegotiate(ssl)<=0) berr_exit("SSL renegotiation error"); if(SSL_do_handshake(ssl)<=0) berr_exit("SSL renegotiation error"); ssl->state=SSL_ST_ACCEPT; if(SSL_do_handshake(ssl)<=0) berr_exit("SSL renegotiation error"); } if((r=BIO_puts (io,"HTTP/1.0 200 OK\r\n"))<=0) err_exit("Write error"); if((r=BIO_puts (io,"Server: EKRServer\r\n\r\n"))<=0) err_exit("Write error"); if((r=BIO_puts (io,"Server test page\r\n"))<=0) err_exit("Write error"); if((r=BIO_flush(io))<0) err_exit("Error flushing BIO"); shutdown: r=SSL_shutdown(ssl); if(!r){ /* If we called SSL_shutdown() first then we always get return value of '0'. In this case, try again, but first send a TCP FIN to trigger the other side's close_notify*/ shutdown(s,1); r=SSL_shutdown(ssl); } switch(r){ case 1: break; /* Success */ case 0: case -1: default: berr_exit("Shutdown failed"); } SSL_free(ssl); close(s); return(0); }
int main(int argc, char **argv) { int sock,s; BIO *sbio; SSL_CTX *ctx; SSL *ssl; int r; pid_t pid; extern char *optarg; int c; while((c=getopt(argc,argv,"cCxna:"))!=-1){ switch(c){ case 'c': client_auth=CLIENT_AUTH_REQUEST; break; case 'C': client_auth=CLIENT_AUTH_REQUIRE; break; case 'x': client_auth=CLIENT_AUTH_REHANDSHAKE; break; case 'n': fork_child=0; break; case 'a': if(!(ciphers=strdup(optarg))) err_exit("Out of memory"); break; } } /* Build our SSL context*/ ctx=initialize_ctx(KEYFILE,PASSWORD); load_dh_params(ctx,DHFILE); SSL_CTX_set_session_id_context(ctx, (void*)&s_server_session_id_context, sizeof s_server_session_id_context); /* Set our cipher list */ if(ciphers){ SSL_CTX_set_cipher_list(ctx,ciphers); } switch(client_auth){ case CLIENT_AUTH_REQUEST: SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,0); break; case CLIENT_AUTH_REQUIRE: SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,0); break; case CLIENT_AUTH_REHANDSHAKE: /* Do nothing */ break; } sock=tcp_listen(); while(1){ if((s=accept(sock,0,0))<0) err_exit("Problem accepting"); if(fork_child && (pid=fork())){ close(s); } else { sbio=BIO_new_socket(s,BIO_NOCLOSE); ssl=SSL_new(ctx); SSL_set_bio(ssl,sbio,sbio); if((r=SSL_accept(ssl)<=0)) berr_exit("SSL accept error"); http_serve(ssl,s); if(fork_child) exit(0); } } destroy_ctx(ctx); exit(0); }
// Send HTTP get and wait for response (SSL/SPP) static int http_request(char *filename, char *proto, bool requestingFile, struct timeval *tvEnd){ char buf[BUFSIZZ]; int r; int len; long bytes_read = 0; // Compute expected data size fSize = atoi(filename); if (fSize == 0 && filename[0] != '0'){ if (requestingFile){ fSize = calculate_file_size(filename); }else{ fSize = strlen("HTTP/1.0 200 OK\r\n"); } } sizeCheck = fSize; experiment_info->file_size = fSize; // Request file (either by name or by size) if (requestingFile){ sendRequest(filename); } // Now read the server's response, assuming that it's terminated by a close while(1){ // SPP read if (strcmp(proto, "spp") == 0){ /* #ifdef DEBUG printf("[DEBUG] SPP_read\n"); #endif */ SPP_SLICE *slice; // slice for SPP_read SPP_CTX *ctx; // context pointer for SPP_read r = SPP_read_record(ssl, buf, BUFSIZZ, &slice, &ctx); #ifdef DEBUG printf("[INFO] Read %d bytes\n", r); #endif if (ssl->read_stats.app_bytes == fSize){ printf("[INFO] Read %d bytes as expected (fSize=%d). Stopping timer\n", ssl->read_stats.app_bytes, fSize); // Stop the timer here (avoid shutdown crap) gettimeofday(tvEnd, NULL); #ifdef VERBOSE fwrite(buf, 1, len, stdout); #endif break; //goto shutdown; } switch(SSL_get_error(ssl, r)){ case SSL_ERROR_NONE: len = r; break; case SSL_ERROR_ZERO_RETURN: goto shutdown; case SSL_ERROR_SYSCALL: fprintf(stderr, "SSL Error: Premature close\n"); goto done; default: berr_exit("SSL read problem"); } } // SSL read else if (strcmp(proto, "ssl") == 0){ /* #ifdef DEBUG printf("[DEBUG] SSL_read\n"); #endif */ r = SSL_read(ssl, buf, BUFSIZZ); #ifdef DEBUG printf("[DEBUG] Read %d bytes\n", r); #endif if (ssl->read_stats.app_bytes == fSize){ printf("[INFO] Read %d bytes as expected (fSize=%d). Stopping timer\n", ssl->read_stats.app_bytes, fSize); gettimeofday(tvEnd, NULL); // Write buf to stdout #ifdef VERBOSE fwrite(buf, 1, len, stdout); #endif break; } switch(SSL_get_error(ssl, r)){ case SSL_ERROR_NONE: len = r; break; case SSL_ERROR_ZERO_RETURN: goto shutdown; case SSL_ERROR_SYSCALL: fprintf(stderr, "SSL Error: Premature close\n"); goto done; default: berr_exit("SSL read problem"); } } // SSL read else if (strcmp(proto, "pln") == 0){ #ifdef DEBUG printf("[DEBUG] Waiting to read bytes in plain mode\n"); #endif r = read(plain_socket, buf, BUFSIZZ); experiment_info->app_bytes_read += r; bytes_read += r; #ifdef DEBUG printf("[DEBUG] Read %d bytes\n", r); #endif if ( r <= 0 || bytes_read == fSize) /* done reading */ { #ifdef DEBUG printf("[DEBUG] File transfer done, done reading socket...\n"); #endif gettimeofday(tvEnd, NULL); // Write buf to stdout #ifdef VERBOSE fwrite(buf, 1, len, stdout); #endif goto done; } } // Write buf to stdout #ifdef VERBOSE fwrite(buf, 1, len, stdout); #endif } shutdown: #ifdef DEBUG printf("[DEBUG] Shutdown was requested\n"); #endif r = SSL_shutdown(ssl); if( !r ){ shutdown(SSL_get_fd(ssl), 1); r = SSL_shutdown(ssl); } switch(r){ case 1: break; // Success case 0: case -1: default: #ifdef DEBUG printf ("Shutdown failed with code %d\n", r); #endif berr_exit("Shutdown failed"); } done: if (stats){ print_stats(ssl); } SSL_free(ssl); return(0); }
int main(int argc, char **argv) { int len, sock, port=PORT, r; SSL_CTX *ctx; SSL *ssl; BIO *sbio; char *ciphers = "SHA1"; char *host=HOST; char buf[256]; char *secret = "What's the question?"; /*Parse command line arguments*/ switch(argc){ case 1: break; case 3: host = argv[1]; port=atoi(argv[2]); if (port<1||port>65535){ fprintf(stderr,"invalid port number"); exit(0); } break; default: printf("Usage: %s server port\n", argv[0]); exit(0); } /* Build our SSL context*/ ctx=initialize_ctx(KEYFILE, PASSWORD); /* Set our cipher list */ if(ciphers){ SSL_CTX_set_cipher_list(ctx, ciphers); } SSL_CTX_set_session_id_context(ctx, (void*)&s_server_session_id_context, sizeof s_server_session_id_context); /* Connect the TCP socket*/ sock=tcp_connect(host,port); /* Connect the SSL socket */ ssl=SSL_new(ctx); sbio=BIO_new_socket(sock,BIO_NOCLOSE); SSL_set_bio(ssl,sbio,sbio); if(SSL_connect(ssl)<=0) { berr_exit(FMT_CONNECT_ERR); } check_cert(ssl, "Bob's Server"); r = SSL_write(ssl, secret, strlen(secret)); switch(SSL_get_error(ssl,r)){ /* We wrote something*/ case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_WRITE: break; case SSL_ERROR_WANT_READ: break; /* Some other error */ default: berr_exit(FMT_INCORRECT_CLOSE); } r=SSL_read(ssl, &buf, 255); switch(SSL_get_error(ssl,r)){ case SSL_ERROR_NONE: len = r; break; case SSL_ERROR_ZERO_RETURN: /* End of data */ berr_exit(FMT_INCORRECT_CLOSE); break; case SSL_ERROR_WANT_READ: break; case SSL_ERROR_WANT_WRITE: break; default: berr_exit(FMT_INCORRECT_CLOSE); } buf[len]='\0'; /* this is how you output something for the marker to pick up */ printf(FMT_OUTPUT, secret, buf); if(SSL_shutdown(ssl) < 0) { berr_exit(FMT_INCORRECT_CLOSE); } SSL_free(ssl); close(sock); destroy_ctx(ctx); return 0; }
/* Read from the keyboard and write to the server Read from the server and write to the keyboard we use select() to multiplex */ void read_write(SSL *ssl,int sock) { int width; int r,c2sl=0,c2s_offset=0; int read_blocked_on_write=0,write_blocked_on_read=0,read_blocked=0; fd_set readfds,writefds; int shutdown_wait=0; char c2s[BUFSIZZ],s2c[BUFSIZZ]; int ofcmode; /*First we make the socket nonblocking*/ ofcmode=fcntl(sock,F_GETFL,0); ofcmode|=O_NDELAY; if(fcntl(sock,F_SETFL,ofcmode)) err_exit("Couldn't make socket nonblocking"); width=sock+1; while(1){ FD_ZERO(&readfds); FD_ZERO(&writefds); FD_SET(sock,&readfds); /* If we're waiting for a read on the socket don't try to write to the server */ if(!write_blocked_on_read){ /* If we have data in the write queue don't try to read from stdin */ if(c2sl || read_blocked_on_write) FD_SET(sock,&writefds); else FD_SET(fileno(stdin),&readfds); } r=select(width,&readfds,&writefds,0,0); if(r==0) continue; /* Now check if there's data to read */ if((FD_ISSET(sock,&readfds) && !write_blocked_on_read) || (read_blocked_on_write && FD_ISSET(sock,&writefds))){ do { read_blocked_on_write=0; read_blocked=0; r=SSL_read(ssl,s2c,BUFSIZZ); switch(SSL_get_error(ssl,r)){ case SSL_ERROR_NONE: /* Note: this call could block, which blocks the entire application. It's arguable this is the right behavior since this is essentially a terminal client. However, in some other applications you would have to prevent this condition */ fwrite(s2c,1,r,stdout); break; case SSL_ERROR_ZERO_RETURN: /* End of data */ if(!shutdown_wait) SSL_shutdown(ssl); goto end; break; case SSL_ERROR_WANT_READ: read_blocked=1; break; /* We get a WANT_WRITE if we're trying to rehandshake and we block on a write during that rehandshake. We need to wait on the socket to be writeable but reinitiate the read when it is */ case SSL_ERROR_WANT_WRITE: read_blocked_on_write=1; break; default: berr_exit("SSL read problem"); } /* We need a check for read_blocked here because SSL_pending() doesn't work properly during the handshake. This check prevents a busy-wait loop around SSL_read() */ } while (SSL_pending(ssl) && !read_blocked); } /* Check for input on the console*/ if(FD_ISSET(fileno(stdin),&readfds)){ c2sl=read(fileno(stdin),c2s,BUFSIZZ); if(c2sl==0){ shutdown_wait=1; if(SSL_shutdown(ssl)) return; } c2s_offset=0; } /* If the socket is writeable... */ if((FD_ISSET(sock,&writefds) && c2sl) || (write_blocked_on_read && FD_ISSET(sock,&readfds))) { write_blocked_on_read=0; /* Try to write */ r=SSL_write(ssl,c2s+c2s_offset,c2sl); switch(SSL_get_error(ssl,r)){ /* We wrote something*/ case SSL_ERROR_NONE: c2sl-=r; c2s_offset+=r; break; /* We would have blocked */ case SSL_ERROR_WANT_WRITE: break; /* We get a WANT_READ if we're trying to rehandshake and we block on write during the current connection. We need to wait on the socket to be readable but reinitiate our write when it is */ case SSL_ERROR_WANT_READ: write_blocked_on_read=1; break; /* Some other error */ default: berr_exit("SSL write problem"); } } } end: SSL_free(ssl); close(sock); return; }
int main(int argc, char** argv) { if(argc<4) { printf("Usage: ./wserver cert-file priv-key-file pub-key-file.\n"); exit(0); } else { CERTFILE = argv[1]; KEYFILE = argv[2]; PUBFILE = argv[3]; const char* PROMPT = "Enter password for Old Key file: "; if(argc == 5) { OLDKEY = argv[4]; PASSWORD = getpass(PROMPT); OLDPASS = (char*) calloc(1, strlen(PASSWORD)+1); strcpy(OLDPASS, PASSWORD); } PROMPT = "Enter password for Key file: "; PASSWORD = getpass(PROMPT); } int sock,s; BIO *sbio; SSL_CTX *ctx; SSL *ssl; int r; pid_t pid; char buf[BUFSIZZ]; char *owner = (char*) calloc(1,256); ctx=initialize_ctx(CERTFILE,KEYFILE,PASSWORD); load_dh_params(ctx,DHFILE); sock=tcp_listen(); if((s=accept(sock,0,0))<0) err_exit("Problem accepting"); sbio=BIO_new_socket(s,BIO_NOCLOSE); ssl=SSL_new(ctx); SSL_set_bio(ssl,sbio,sbio); SSL_set_verify(ssl,SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,0); if((r=SSL_accept(ssl)<=0)) berr_exit("SSL accept error"); if(check_cert(ssl, ctx, &owner)<=0) { send_data(ssl, "Revoked"); printf("Connection Closed.\n"); close_SSL(ssl, sock); destroy_ctx(ctx); exit(0); } send_data(ssl, "Approved"); printf("User connected: %s\n", owner); if((pid=fork())){ close(s); } else { if(argc == 5) {recrypt();} while(1){ memset((void*)buf, 0, BUFSIZZ); if(rec_data(buf, ssl)>0) { printf("Command received: %s\n", buf); if(starts_with(buf, "PUT")){ put_file(ssl, buf, owner); } else if(starts_with(buf, "GET")){ get_file(ssl, buf, owner); } else if(starts_with(buf, "DELEGATE")){ delegate(ssl, buf, owner); } else if(starts_with(buf, "END")){ close_SSL(ssl, sock); break; } else { printf("Command not recognized\n"); } } else{ perror("Error receiving command\n"); break; } } } destroy_ctx(ctx); exit(0); }
/* Read from the keyboard and write to the server Read from the server and write to the keyboard */ void read_write (SSL *ssl, int sock) { int width; int r,c2sl=0,c2s_offset=0; fd_set readfds,writefds; int shutdown_wait=0; char c2s[BUFSIZZ],s2c[BUFSIZZ]; int ofcmode; /*First we make the socket nonblocking*/ ofcmode=fcntl(sock,F_GETFL,0); ofcmode|=O_NDELAY; if(fcntl(sock,F_SETFL,ofcmode)) err_exit("Couldn't make socket nonblocking"); width=sock+1; while(1){ FD_ZERO(&readfds); FD_ZERO(&writefds); FD_SET(sock,&readfds); /*If we've still got data to write then don't try to read*/ if(c2sl) FD_SET(sock,&writefds); else FD_SET(fileno(stdin),&readfds); r=select(width,&readfds,&writefds,0,0); if(r==0) continue; /* Now check if there's data to read */ if(FD_ISSET(sock,&readfds)){ do { r=SSL_read(ssl,s2c,BUFSIZZ); switch(SSL_get_error(ssl,r)){ case SSL_ERROR_NONE: fwrite(s2c,1,r,stdout); break; case SSL_ERROR_ZERO_RETURN: /* End of data */ if(!shutdown_wait) SSL_shutdown(ssl); goto end; break; case SSL_ERROR_WANT_READ: break; default: berr_exit("SSL read problem"); } } while (SSL_pending(ssl)); } /* Check for input on the console*/ if(FD_ISSET(fileno(stdin),&readfds)){ c2sl=read(fileno(stdin),c2s,BUFSIZZ); if(c2sl==0){ shutdown_wait=1; if(SSL_shutdown(ssl)) return; } c2s_offset=0; } /* If we've got data to write then try to write it*/ if(c2sl && FD_ISSET(sock,&writefds)){ r=SSL_write(ssl,c2s+c2s_offset,c2sl); switch(SSL_get_error(ssl,r)){ /* We wrote something*/ case SSL_ERROR_NONE: c2sl-=r; c2s_offset+=r; break; /* We would have blocked */ case SSL_ERROR_WANT_WRITE: break; /* Some other error */ default: berr_exit("SSL write problem"); } } } end: SSL_free(ssl); close(sock); return; }