OpenSSL::OpenSSL( TLSHandler *th, const std::string& server ) : TLSBase( th, server ), m_ssl( 0 ), m_ctx( 0 ), m_buf( 0 ), m_bufsize( 17000 ) { m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) ); SSL_library_init(); SSL_COMP_add_compression_method( 1, COMP_zlib() ); m_ctx = SSL_CTX_new( TLSv1_client_method() ); if( !m_ctx ) return; if( !SSL_CTX_set_cipher_list( m_ctx, "HIGH:MEDIUM:AES:@STRENGTH" ) ) return; m_ssl = SSL_new( m_ctx ); SSL_set_connect_state( m_ssl ); if( !BIO_new_bio_pair( &m_ibio, 0, &m_nbio, 0 ) ) { return; } SSL_set_bio( m_ssl, m_ibio, m_ibio ); SSL_set_mode( m_ssl, SSL_MODE_AUTO_RETRY ); }
bool OpenSSL::init() { if( m_initLib ) SSL_library_init(); SSL_COMP_add_compression_method( 1, COMP_zlib() ); m_ctx = SSL_CTX_new( TLSv1_client_method() ); if( !m_ctx ) return false; if( !SSL_CTX_set_cipher_list( m_ctx, "HIGH:MEDIUM:AES:@STRENGTH" ) ) return false; m_ssl = SSL_new( m_ctx ); SSL_set_connect_state( m_ssl ); if( !BIO_new_bio_pair( &m_ibio, 0, &m_nbio, 0 ) ) return false; SSL_set_bio( m_ssl, m_ibio, m_ibio ); SSL_set_mode( m_ssl, SSL_MODE_AUTO_RETRY ); m_valid = true; return true; }
static int init_ssl_socket( pn_ssl_t *ssl ) { if (ssl->ssl) return 0; if (!ssl->domain) return -1; ssl->ssl = SSL_new(ssl->domain->ctx); if (!ssl->ssl) { _log_error( "SSL socket setup failure.\n" ); return -1; } // store backpointer to pn_ssl_t in SSL object: SSL_set_ex_data(ssl->ssl, ssl_ex_data_index, ssl); #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME if (ssl->peer_hostname && ssl->domain->mode == PN_SSL_MODE_CLIENT) { SSL_set_tlsext_host_name(ssl->ssl, ssl->peer_hostname); } #endif // restore session, if available if (ssl->session_id) { pn_ssl_session_t *ssn = ssn_cache_find( ssl->domain, ssl->session_id ); if (ssn) { _log( ssl, "Restoring previous session id=%s\n", ssn->id ); int rc = SSL_set_session( ssl->ssl, ssn->session ); if (rc != 1) { _log( ssl, "Session restore failed, id=%s\n", ssn->id ); } LL_REMOVE( ssl->domain, ssn_cache, ssn ); ssl_session_free( ssn ); } } // now layer a BIO over the SSL socket ssl->bio_ssl = BIO_new(BIO_f_ssl()); if (!ssl->bio_ssl) { _log_error( "BIO setup failure.\n" ); return -1; } (void)BIO_set_ssl(ssl->bio_ssl, ssl->ssl, BIO_NOCLOSE); // create the "lower" BIO "pipe", and attach it below the SSL layer if (!BIO_new_bio_pair(&ssl->bio_ssl_io, 0, &ssl->bio_net_io, 0)) { _log_error( "BIO setup failure.\n" ); return -1; } SSL_set_bio(ssl->ssl, ssl->bio_ssl_io, ssl->bio_ssl_io); if (ssl->domain->mode == PN_SSL_MODE_SERVER) { SSL_set_accept_state(ssl->ssl); BIO_set_ssl_mode(ssl->bio_ssl, 0); // server mode _log( ssl, "Server SSL socket created.\n" ); } else { // client mode SSL_set_connect_state(ssl->ssl); BIO_set_ssl_mode(ssl->bio_ssl, 1); // client mode _log( ssl, "Client SSL socket created.\n" ); } return 0; }
TlsThread::TlsThread( bool asClient ) : d( new TlsThreadData ) { if ( !ctx ) setup(); d->ssl = ::SSL_new( ctx ); if ( asClient ) SSL_set_connect_state( d->ssl ); else SSL_set_accept_state( d->ssl ); if ( !BIO_new_bio_pair( &d->sslBio, bs, &d->networkBio, bs ) ) { // an error. hm? } ::SSL_set_bio( d->ssl, d->sslBio, d->sslBio ); d->ctrb = (char*)Allocator::alloc( bs, 0 ); d->ctwb = (char*)Allocator::alloc( bs, 0 ); d->encrb = (char*)Allocator::alloc( bs, 0 ); d->encwb = (char*)Allocator::alloc( bs, 0 ); int r = pthread_create( &d->thread, 0, trampoline, (void*)this ); if ( r ) { log( "pthread_create returned nonzero (" + fn( r ) + ")" ); d->broken = true; ::SSL_free( d->ssl ); d->ssl = 0; } }
evt_tls_t *getSSL(evt_ctx_t *d_eng) { evt_tls_t *con = malloc(sizeof(evt_tls_t)); if ( !con ) { return NULL; } memset( con, 0, sizeof *con); SSL *ssl = SSL_new(d_eng->ctx); if ( !ssl ) { return NULL; } con->ssl = ssl; //use default buf size for now. BIO_new_bio_pair(&(con->ssl_bio_), 0, &(con->app_bio_), 0); SSL_set_bio(con->ssl, con->ssl_bio_, con->ssl_bio_); QUEUE_INIT(&(con->q)); QUEUE_INSERT_TAIL(&(d_eng->live_con), &(con->q)); con->writer = d_eng->writer; return con; }
lwp_sslclient lwp_sslclient_new (SSL_CTX * server_context, lw_stream socket, lwp_sslclient_on_handshook on_handshook, void * tag) { lwp_sslclient ctx = calloc (sizeof (*ctx), 1); if (!ctx) return 0; ctx->write_condition = -1; #ifdef _lacewing_npn *ctx->npn = 0; #endif ctx->server_context = server_context; ctx->ssl = SSL_new (server_context); ctx->on_handshook = on_handshook; ctx->tag = tag; /* TODO : I'm not really happy with the extra layer of buffering * that BIO pairs introduce. Is there a better way to do this? */ BIO_new_bio_pair (&ctx->bio_internal, 0, &ctx->bio_external, 0); SSL_set_bio (ctx->ssl, ctx->bio_internal, ctx->bio_internal); SSL_set_accept_state (ctx->ssl); lwp_stream_init (&ctx->upstream, &def_upstream, 0); lwp_stream_init (&ctx->downstream, &def_downstream, 0); /* Retain our streams indefinitely, since we'll be in charge of releasing * their memory. This doesn't stop stream_delete from working. */ lwp_retain (&ctx->upstream); lwp_retain (&ctx->downstream); lw_stream_add_filter_upstream (socket, &ctx->upstream, lw_false, lw_false); lw_stream_add_filter_downstream (socket, &ctx->downstream, lw_false, lw_false); pump (ctx); assert (! (ctx->flags & lwp_sslclient_flag_dead)); return ctx; }
SSLConnection::SSLConnection(SSL_CTX* context, Socket* sock) : socket(sock) { // This just ensures that SSL multithreading support is set up for this thread, // if it's not already. SSLThreadInfo::get(); ssl = SSL_new(context); std::string sslErr = NULL != getSSLManager() ? getSSLManager()->getSSLErrorMessage(ERR_get_error()) : ""; massert(15861, "Error creating new SSL object " + sslErr, ssl); BIO_new_bio_pair(&internalBIO, BUFFER_SIZE, &networkBIO, BUFFER_SIZE); SSL_set_bio(ssl, internalBIO, internalBIO); }
bool OpenSSLBase::init( const std::string& clientKey, const std::string& clientCerts, const StringList& cacerts ) { #if defined OPENSSL_VERSION_NUMBER && ( OPENSSL_VERSION_NUMBER < 0x10100000 ) if( m_initLib ) SSL_library_init(); #endif // OPENSSL_VERSION_NUMBER < 0x10100000 SSL_COMP_add_compression_method( 193, COMP_zlib() ); OpenSSL_add_all_algorithms(); if( !setType() ) //inits m_ctx return false; setClientCert( clientKey, clientCerts ); setCACerts( cacerts ); if( !SSL_CTX_set_cipher_list( m_ctx, "HIGH:MEDIUM:AES:@STRENGTH" ) ) return false; m_ssl = SSL_new( m_ctx ); if( !m_ssl ) return false; if( !BIO_new_bio_pair( &m_ibio, 0, &m_nbio, 0 ) ) return false; SSL_set_bio( m_ssl, m_ibio, m_ibio ); SSL_set_mode( m_ssl, SSL_MODE_AUTO_RETRY | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE ); ERR_load_crypto_strings(); SSL_load_error_strings(); if( !privateInit() ) return false; m_valid = true; return true; }
static int openssl_iostream_create(struct ssl_iostream_context *ctx, const char *source, const struct ssl_iostream_settings *set, struct istream **input, struct ostream **output, struct ssl_iostream **iostream_r) { struct ssl_iostream *ssl_io; SSL *ssl; BIO *bio_int, *bio_ext; int ret; ssl = SSL_new(ctx->ssl_ctx); if (ssl == NULL) { i_error("SSL_new() failed: %s", openssl_iostream_error()); return -1; } /* BIO pairs use default buffer sizes (17 kB in OpenSSL 0.9.8e). Each of the BIOs have one "write buffer". BIO_write() copies data to them, while BIO_read() reads from the other BIO's write buffer into the given buffer. The bio_int is used by OpenSSL and bio_ext is used by this library. */ if (BIO_new_bio_pair(&bio_int, 0, &bio_ext, 0) != 1) { i_error("BIO_new_bio_pair() failed: %s", openssl_iostream_error()); SSL_free(ssl); return -1; } ssl_io = i_new(struct ssl_iostream, 1); ssl_io->refcount = 1; ssl_io->ctx = ctx; ssl_io->ssl = ssl; ssl_io->bio_ext = bio_ext; ssl_io->plain_input = *input; ssl_io->plain_output = *output; ssl_io->source = i_strdup(source); /* bio_int will be freed by SSL_free() */ SSL_set_bio(ssl_io->ssl, bio_int, bio_int); SSL_set_ex_data(ssl_io->ssl, dovecot_ssl_extdata_index, ssl_io); T_BEGIN { ret = openssl_iostream_set(ssl_io, set); } T_END; if (ret < 0) { openssl_iostream_free(ssl_io); return -1; } o_stream_uncork(ssl_io->plain_output); *input = openssl_i_stream_create_ssl(ssl_io); *output = openssl_o_stream_create_ssl(ssl_io); i_stream_set_name(*input, t_strconcat("SSL ", i_stream_get_name(ssl_io->plain_input), NULL)); o_stream_set_name(*output, t_strconcat("SSL ", o_stream_get_name(ssl_io->plain_output), NULL)); if (ssl_io->plain_output->real_stream->error_handling_disabled) o_stream_set_no_error_handling(*output, TRUE); ssl_io->ssl_output = *output; *iostream_r = ssl_io; return 0; }
static int test_zero_copy_bio_pairs(void) { /* Test read and write, especially triggering the ring buffer wrap-around.*/ BIO* bio1; BIO* bio2; size_t i, j; uint8_t bio1_application_send_buffer[1024]; uint8_t bio2_application_recv_buffer[1024]; size_t total_read = 0; size_t total_write = 0; uint8_t* write_buf; size_t write_buf_offset; size_t available_bytes; size_t bytes_left; const size_t kLengths[] = {254, 255, 256, 257, 510, 511, 512, 513}; /* These trigger ring buffer wrap around. */ const size_t kPartialLengths[] = {0, 1, 2, 3, 128, 255, 256, 257, 511, 512}; static const size_t kBufferSize = 512; srand(1); for (i = 0; i < sizeof(bio1_application_send_buffer); i++) { bio1_application_send_buffer[i] = rand() & 255; } /* Transfer bytes from bio1_application_send_buffer to * bio2_application_recv_buffer in various ways. */ for (i = 0; i < sizeof(kLengths) / sizeof(kLengths[0]); i++) { for (j = 0; j < sizeof(kPartialLengths) / sizeof(kPartialLengths[0]); j++) { total_write = 0; total_read = 0; BIO_new_bio_pair(&bio1, kBufferSize, &bio2, kBufferSize); total_write += bio_write_zero_copy_wrapper( bio1, bio1_application_send_buffer, kLengths[i]); /* This tests interleaved read/write calls. Do a read between zero copy * write calls. */ if (!BIO_zero_copy_get_write_buf(bio1, &write_buf, &write_buf_offset, &available_bytes)) { return 0; } /* Free kPartialLengths[j] bytes in the beginning of bio1 write buffer. * This enables ring buffer wrap around for the next write. */ total_read += BIO_read(bio2, bio2_application_recv_buffer + total_read, kPartialLengths[j]); size_t interleaved_write_len = MIN(kPartialLengths[j], available_bytes); /* Write the data for the interleaved write call. If the buffer becomes * empty after a read, the write offset is normally set to 0. Check that * this does not happen for interleaved read/write and that * |write_buf_offset| is still valid. */ memcpy(write_buf + write_buf_offset, bio1_application_send_buffer + total_write, interleaved_write_len); if (BIO_zero_copy_get_write_buf_done(bio1, interleaved_write_len)) { total_write += interleaved_write_len; } /* Do another write in case |write_buf_offset| was wrapped */ total_write += bio_write_zero_copy_wrapper( bio1, bio1_application_send_buffer + total_write, kPartialLengths[j] - interleaved_write_len); /* Drain the rest. */ bytes_left = BIO_pending(bio2); total_read += bio_read_zero_copy_wrapper( bio2, bio2_application_recv_buffer + total_read, bytes_left); BIO_free(bio1); BIO_free(bio2); if (total_read != total_write) { fprintf(stderr, "Lengths not equal in round (%u, %u)\n", (unsigned)i, (unsigned)j); return 0; } if (total_read > kLengths[i] + kPartialLengths[j]) { fprintf(stderr, "Bad lengths in round (%u, %u)\n", (unsigned)i, (unsigned)j); return 0; } if (memcmp(bio1_application_send_buffer, bio2_application_recv_buffer, total_read) != 0) { fprintf(stderr, "Buffers not equal in round (%u, %u)\n", (unsigned)i, (unsigned)j); return 0; } } } return 1; }
/* * This is the actual startup routine for the connection. We expect that the * buffers are flushed and the "220 Ready to start TLS" was received by us, * so that we can immediately start the TLS handshake process. */ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) { const char *myname = "tls_client_start"; int sts; int protomask; const char *cipher_list; SSL_SESSION *session; SSL_CIPHER *cipher; X509 *peercert; TLS_SESS_STATE *TLScontext; TLS_APPL_STATE *app_ctx = props->ctx; ACL_VSTRING *myserverid; if (props->log_level >= 1) acl_msg_info("%s(%d): setting up TLS connection to %s", myname, __LINE__, props->namaddr); /* * First make sure we have valid protocol and cipher parameters * * The cipherlist will be applied to the global SSL context, where it can be * repeatedly reset if necessary, but the protocol restrictions will be * is applied to the SSL connection, because protocol restrictions in the * global context cannot be cleared. */ /* * OpenSSL will ignore cached sessions that use the wrong protocol. So we * do not need to filter out cached sessions with the "wrong" protocol, * rather OpenSSL will simply negotiate a new session. * * Still, we salt the session lookup key with the protocol list, so that * sessions found in the cache are always acceptable. */ protomask = tls_protocol_mask(props->protocols); if (protomask == TLS_PROTOCOL_INVALID) { /* tls_protocol_mask() logs no warning. */ acl_msg_warn("%s(%d): nameaddr: %s: Invalid TLS protocol list \"%s\": aborting TLS session", myname, __LINE__, props->namaddr, props->protocols); return (0); } myserverid = acl_vstring_alloc(100); acl_vstring_sprintf_append(myserverid, "%s&p=%d", props->serverid, protomask); /* * Per session cipher selection for sessions with mandatory encryption * * By the time a TLS client is negotiating ciphers it has already offered to * re-use a session, it is too late to renege on the offer. So we must * not attempt to re-use sessions whose ciphers are too weak. We salt the * session lookup key with the cipher list, so that sessions found in the * cache are always acceptable. */ cipher_list = tls_set_ciphers(app_ctx, "TLS", props->cipher_grade, props->cipher_exclusions); if (cipher_list == 0) { acl_msg_warn("%s(%d): %s: %s: aborting TLS session", myname, __LINE__, props->namaddr, acl_vstring_str(app_ctx->why)); acl_vstring_free(myserverid); return (0); } if (props->log_level >= 2) acl_msg_info("%s(%d): %s: TLS cipher list \"%s\"", myname, __LINE__, props->namaddr, cipher_list); acl_vstring_sprintf_append(myserverid, "&c=%s", cipher_list); /* * Allocate a new TLScontext for the new connection and get an SSL * structure. Add the location of TLScontext to the SSL to later retrieve * the information inside the tls_verify_certificate_callback(). * * If session caching was enabled when TLS was initialized, the cache type * is stored in the client SSL context. */ TLScontext = tls_alloc_sess_context(props->log_level, props->namaddr); TLScontext->cache_type = app_ctx->cache_type; TLScontext->serverid = acl_vstring_export(myserverid); if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) { acl_msg_warn("%s(%d): Could not allocate 'TLScontext->con' with SSL_new()", myname, __LINE__); tls_print_errors(); tls_free_context(TLScontext); return (0); } if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) { acl_msg_warn("%s(%d): Could not set application data for 'TLScontext->con'", myname, __LINE__); tls_print_errors(); tls_free_context(TLScontext); return (0); } /* * Apply session protocol restrictions. */ if (protomask != 0) SSL_set_options(TLScontext->con, ((protomask & TLS_PROTOCOL_TLSv1) ? SSL_OP_NO_TLSv1 : 0L) | ((protomask & TLS_PROTOCOL_SSLv3) ? SSL_OP_NO_SSLv3 : 0L) | ((protomask & TLS_PROTOCOL_SSLv2) ? SSL_OP_NO_SSLv2 : 0L)); /* * The TLS connection is realized by a BIO_pair, so obtain the pair. * * XXX There is no need to make internal_bio a member of the TLScontext * structure. It will be attached to TLScontext->con, and destroyed along * with it. The network_bio, however, needs to be freed explicitly. */ if (!BIO_new_bio_pair(&TLScontext->internal_bio, TLS_BIO_BUFSIZE, &TLScontext->network_bio, TLS_BIO_BUFSIZE)) { acl_msg_warn("%s(%d): Could not obtain BIO_pair", myname, __LINE__); tls_print_errors(); tls_free_context(TLScontext); return (0); } /* * XXX To avoid memory leaks we must always call SSL_SESSION_free() after * calling SSL_set_session(), regardless of whether or not the session * will be reused. */ if (TLScontext->cache_type) { session = load_clnt_session(TLScontext); if (session) { SSL_set_session(TLScontext->con, session); SSL_SESSION_free(session); /* 200411 */ #if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L) /* * Ugly Hack: OpenSSL before 0.9.6a does not store the verify * result in sessions for the client side. We modify the session * directly which is version specific, but this bug is version * specific, too. * * READ: 0-09-06-01-1 = 0-9-6-a-beta1: all versions before beta1 * have this bug, it has been fixed during development of 0.9.6a. * The development version of 0.9.7 can have this bug, too. It * has been fixed on 2000/11/29. */ SSL_set_verify_result(TLScontext->con, session->verify_result); #endif } } /* * Before really starting anything, try to seed the PRNG a little bit * more. */ tls_int_seed(); if (var_tls_daemon_rand_bytes > 0) (void) tls_ext_seed(var_tls_daemon_rand_bytes); /* * Initialize the SSL connection to connect state. This should not be * necessary anymore since 0.9.3, but the call is still in the library * and maintaining compatibility never hurts. */ SSL_set_connect_state(TLScontext->con); /* * Connect the SSL connection with the Postfix side of the BIO-pair for * reading and writing. */ SSL_set_bio(TLScontext->con, TLScontext->internal_bio, TLScontext->internal_bio); /* * If the debug level selected is high enough, all of the data is dumped: * 3 will dump the SSL negotiation, 4 will dump everything. * * We do have an SSL_set_fd() and now suddenly a BIO_ routine is called? * Well there is a BIO below the SSL routines that is automatically * created for us, so we can use it for debugging purposes. */ if (props->log_level >= 3) BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb); /* * Start TLS negotiations. This process is a black box that invokes our * call-backs for certificate verification. * * Error handling: If the SSL handhake fails, we print out an error message * and remove all TLS state concerning this session. */ sts = tls_bio_connect(ACL_VSTREAM_SOCK(props->stream), props->timeout, TLScontext); if (sts <= 0) { acl_msg_info("%s(%d): SSL_connect error to %s: %d", myname, __LINE__, props->namaddr, sts); tls_print_errors(); uncache_session(app_ctx->ssl_ctx, TLScontext); tls_free_context(TLScontext); return (0); } /* Only log_level==4 dumps everything */ if (props->log_level < 4) BIO_set_callback(SSL_get_rbio(TLScontext->con), 0); /* * The caller may want to know if this session was reused or if a new * session was negotiated. */ TLScontext->session_reused = SSL_session_reused(TLScontext->con); if (props->log_level >= 2 && TLScontext->session_reused) acl_msg_info("%s(%d): %s: Reusing old session", myname, __LINE__, TLScontext->namaddr); /* * Do peername verification if requested and extract useful information * from the certificate for later use. */ if ((peercert = SSL_get_peer_certificate(TLScontext->con)) != 0) { TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT; /* * Peer name or fingerprint verification as requested. * Unconditionally set peer_CN, issuer_CN and peer_fingerprint. */ verify_extract_name(TLScontext, peercert, props); verify_extract_print(TLScontext, peercert, props); X509_free(peercert); } else { TLScontext->issuer_CN = acl_mystrdup(""); TLScontext->peer_CN = acl_mystrdup(""); TLScontext->peer_fingerprint = acl_mystrdup(""); } /* * Finally, collect information about protocol and cipher for logging */ TLScontext->protocol = SSL_get_version(TLScontext->con); cipher = SSL_get_current_cipher(TLScontext->con); TLScontext->cipher_name = SSL_CIPHER_get_name(cipher); TLScontext->cipher_usebits = SSL_CIPHER_get_bits(cipher, &(TLScontext->cipher_algbits)); /* * The TLS engine is active. Switch to the tls_timed_read/write() * functions and make the TLScontext available to those functions. */ tls_stream_start(props->stream, TLScontext); /* * All the key facts in a single log entry. */ if (props->log_level >= 1) acl_msg_info("%s(%d): %s TLS connection established to %s: %s with cipher %s " "(%d/%d bits)", myname, __LINE__, TLS_CERT_IS_MATCHED(TLScontext) ? "Verified" : TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted", props->namaddr, TLScontext->protocol, TLScontext->cipher_name, TLScontext->cipher_usebits, TLScontext->cipher_algbits); tls_int_seed(); return (TLScontext); }
int krypt_secure_connection(krypt_t *kconn, uint8_t protocol, uint8_t conn_type, uint8_t security_level) { switch (protocol) { case KRYPT_TLS: kconn->ctx = SSL_CTX_new(TLSv1_method()); break; default: jlog(L_ERROR, "unknown protocol"); return -1; } if (kconn->ctx == NULL) { jlog(L_ERROR, "unable to create SSL context"); ssl_error_stack(); return -1; } SSL_CTX_set_session_id_context(kconn->ctx, (void*)&s_server_session_id_context, sizeof(s_server_session_id_context)); if (security_level == KRYPT_ADH) krypt_set_adh(kconn); // Create the BIO pair BIO_new_bio_pair(&kconn->internal_bio, 0, &kconn->network_bio, 0); // Create the SSL object kconn->ssl = SSL_new(kconn->ctx); SSL_set_bio(kconn->ssl, kconn->internal_bio, kconn->internal_bio); SSL_set_mode(kconn->ssl, SSL_MODE_AUTO_RETRY); if (security_level == KRYPT_RSA) krypt_set_rsa(kconn); kconn->conn_type = conn_type; switch (conn_type) { case KRYPT_SERVER: jlog(L_NOTICE, "connection type server"); SSL_set_accept_state(kconn->ssl); break; case KRYPT_CLIENT: jlog(L_NOTICE, "connection type client"); SSL_set_connect_state(kconn->ssl); break; default: jlog(L_ERROR, "unknown connection type"); return -1; } kconn->status = KRYPT_HANDSHAKE; return 0; }
int doit_biopair(SSL *s_ssl, SSL *c_ssl, long count, clock_t *s_time, clock_t *c_time) { long cw_num = count, cr_num = count, sw_num = count, sr_num = count; BIO *s_ssl_bio = NULL, *c_ssl_bio = NULL; BIO *server = NULL, *server_io = NULL, *client = NULL, *client_io = NULL; int ret = 1; size_t bufsiz = 256; /* small buffer for testing */ if (!BIO_new_bio_pair(&server, bufsiz, &server_io, bufsiz)) goto err; if (!BIO_new_bio_pair(&client, bufsiz, &client_io, bufsiz)) goto err; s_ssl_bio = BIO_new(BIO_f_ssl()); if (!s_ssl_bio) goto err; c_ssl_bio = BIO_new(BIO_f_ssl()); if (!c_ssl_bio) goto err; SSL_set_connect_state(c_ssl); SSL_set_bio(c_ssl, client, client); (void)BIO_set_ssl(c_ssl_bio, c_ssl, BIO_NOCLOSE); SSL_set_accept_state(s_ssl); SSL_set_bio(s_ssl, server, server); (void)BIO_set_ssl(s_ssl_bio, s_ssl, BIO_NOCLOSE); do { /* c_ssl_bio: SSL filter BIO * * client: pseudo-I/O for SSL library * * client_io: client's SSL communication; usually to be * relayed over some I/O facility, but in this * test program, we're the server, too: * * server_io: server's SSL communication * * server: pseudo-I/O for SSL library * * s_ssl_bio: SSL filter BIO * * The client and the server each employ a "BIO pair": * client + client_io, server + server_io. * BIO pairs are symmetric. A BIO pair behaves similar * to a non-blocking socketpair (but both endpoints must * be handled by the same thread). * [Here we could connect client and server to the ends * of a single BIO pair, but then this code would be less * suitable as an example for BIO pairs in general.] * * Useful functions for querying the state of BIO pair endpoints: * * BIO_ctrl_pending(bio) number of bytes we can read now * BIO_ctrl_get_read_request(bio) number of bytes needed to fulfil * other side's read attempt * BIO_ctrl_get_write_guarantee(bio) number of bytes we can write now * * ..._read_request is never more than ..._write_guarantee; * it depends on the application which one you should use. */ /* We have non-blocking behaviour throughout this test program, but * can be sure that there is *some* progress in each iteration; so * we don't have to worry about ..._SHOULD_READ or ..._SHOULD_WRITE * -- we just try everything in each iteration */ { /* CLIENT */ MS_STATIC char cbuf[1024*8]; int i, r; clock_t c_clock = clock(); memset(cbuf, 0, sizeof(cbuf)); if (debug) if (SSL_in_init(c_ssl)) printf("client waiting in SSL_connect - %s\n", SSL_state_string_long(c_ssl)); if (cw_num > 0) { /* Write to server. */ if (cw_num > (long)sizeof cbuf) i = sizeof cbuf; else i = (int)cw_num; r = BIO_write(c_ssl_bio, cbuf, i); if (r < 0) { if (!BIO_should_retry(c_ssl_bio)) { fprintf(stderr,"ERROR in CLIENT\n"); goto err; } /* BIO_should_retry(...) can just be ignored here. * The library expects us to call BIO_write with * the same arguments again, and that's what we will * do in the next iteration. */ } else if (r == 0) { fprintf(stderr,"SSL CLIENT STARTUP FAILED\n"); goto err; } else { if (debug) printf("client wrote %d\n", r); cw_num -= r; } } if (cr_num > 0) { /* Read from server. */ r = BIO_read(c_ssl_bio, cbuf, sizeof(cbuf)); if (r < 0) { if (!BIO_should_retry(c_ssl_bio)) { fprintf(stderr,"ERROR in CLIENT\n"); goto err; } /* Again, "BIO_should_retry" can be ignored. */ } else if (r == 0) { fprintf(stderr,"SSL CLIENT STARTUP FAILED\n"); goto err; } else { if (debug) printf("client read %d\n", r); cr_num -= r; } } /* c_time and s_time increments will typically be very small * (depending on machine speed and clock tick intervals), * but sampling over a large number of connections should * result in fairly accurate figures. We cannot guarantee * a lot, however -- if each connection lasts for exactly * one clock tick, it will be counted only for the client * or only for the server or even not at all. */ *c_time += (clock() - c_clock); } { /* SERVER */ MS_STATIC char sbuf[1024*8]; int i, r; clock_t s_clock = clock(); memset(sbuf, 0, sizeof(sbuf)); if (debug) if (SSL_in_init(s_ssl)) printf("server waiting in SSL_accept - %s\n", SSL_state_string_long(s_ssl)); if (sw_num > 0) { /* Write to client. */ if (sw_num > (long)sizeof sbuf) i = sizeof sbuf; else i = (int)sw_num; r = BIO_write(s_ssl_bio, sbuf, i); if (r < 0) { if (!BIO_should_retry(s_ssl_bio)) { fprintf(stderr,"ERROR in SERVER\n"); goto err; } /* Ignore "BIO_should_retry". */ } else if (r == 0) { fprintf(stderr,"SSL SERVER STARTUP FAILED\n"); goto err; } else { if (debug) printf("server wrote %d\n", r); sw_num -= r; } } if (sr_num > 0) { /* Read from client. */ r = BIO_read(s_ssl_bio, sbuf, sizeof(sbuf)); if (r < 0) { if (!BIO_should_retry(s_ssl_bio)) { fprintf(stderr,"ERROR in SERVER\n"); goto err; } /* blah, blah */ } else if (r == 0) { fprintf(stderr,"SSL SERVER STARTUP FAILED\n"); goto err; } else { if (debug) printf("server read %d\n", r); sr_num -= r; } } *s_time += (clock() - s_clock); } { /* "I/O" BETWEEN CLIENT AND SERVER. */ size_t r1, r2; BIO *io1 = server_io, *io2 = client_io; /* we use the non-copying interface for io1 * and the standard BIO_write/BIO_read interface for io2 */ static int prev_progress = 1; int progress = 0; /* io1 to io2 */ do { size_t num; int r; r1 = BIO_ctrl_pending(io1); r2 = BIO_ctrl_get_write_guarantee(io2); num = r1; if (r2 < num) num = r2; if (num) { char *dataptr; if (INT_MAX < num) /* yeah, right */ num = INT_MAX; r = BIO_nread(io1, &dataptr, (int)num); assert(r > 0); assert(r <= (int)num); /* possibly r < num (non-contiguous data) */ num = r; r = BIO_write(io2, dataptr, (int)num); if (r != (int)num) /* can't happen */ { fprintf(stderr, "ERROR: BIO_write could not write " "BIO_ctrl_get_write_guarantee() bytes"); goto err; } progress = 1; if (debug) printf((io1 == client_io) ? "C->S relaying: %d bytes\n" : "S->C relaying: %d bytes\n", (int)num); } } while (r1 && r2); /* io2 to io1 */ { size_t num; int r; r1 = BIO_ctrl_pending(io2); r2 = BIO_ctrl_get_read_request(io1); /* here we could use ..._get_write_guarantee instead of * ..._get_read_request, but by using the latter * we test restartability of the SSL implementation * more thoroughly */ num = r1; if (r2 < num) num = r2; if (num) { char *dataptr; if (INT_MAX < num) num = INT_MAX; if (num > 1) --num; /* test restartability even more thoroughly */ r = BIO_nwrite0(io1, &dataptr); assert(r > 0); if (r < (int)num) num = r; r = BIO_read(io2, dataptr, (int)num); if (r != (int)num) /* can't happen */ { fprintf(stderr, "ERROR: BIO_read could not read " "BIO_ctrl_pending() bytes"); goto err; } progress = 1; r = BIO_nwrite(io1, &dataptr, (int)num); if (r != (int)num) /* can't happen */ { fprintf(stderr, "ERROR: BIO_nwrite() did not accept " "BIO_nwrite0() bytes"); goto err; } if (debug) printf((io2 == client_io) ? "C->S relaying: %d bytes\n" : "S->C relaying: %d bytes\n", (int)num); } } /* no loop, BIO_ctrl_get_read_request now returns 0 anyway */ if (!progress && !prev_progress) if (cw_num > 0 || cr_num > 0 || sw_num > 0 || sr_num > 0) { fprintf(stderr, "ERROR: got stuck\n"); if (strcmp("SSLv2", SSL_get_version(c_ssl)) == 0) { fprintf(stderr, "This can happen for SSL2 because " "CLIENT-FINISHED and SERVER-VERIFY are written \n" "concurrently ..."); if (strncmp("2SCF", SSL_state_string(c_ssl), 4) == 0 && strncmp("2SSV", SSL_state_string(s_ssl), 4) == 0) { fprintf(stderr, " ok.\n"); goto end; } } fprintf(stderr, " ERROR.\n"); goto err; } prev_progress = progress; } } while (cw_num > 0 || cr_num > 0 || sw_num > 0 || sr_num > 0); if (verbose) print_details(c_ssl, "DONE via BIO pair: "); end: ret = 0; err: ERR_print_errors(bio_err); if (server) BIO_free(server); if (server_io) BIO_free(server_io); if (client) BIO_free(client); if (client_io) BIO_free(client_io); if (s_ssl_bio) BIO_free(s_ssl_bio); if (c_ssl_bio) BIO_free(c_ssl_bio); return ret; }