static void set_server_temporary_key_info(TLS_REC *tls, SSL *ssl) { #ifdef SSL_get_server_tmp_key /* Show ephemeral key information. */ EVP_PKEY *ephemeral_key = NULL; /* OPENSSL_NO_EC is for solaris 11.3 (2016), github ticket #598 */ #ifndef OPENSSL_NO_EC EC_KEY *ec_key = NULL; #endif char *ephemeral_key_algorithm = NULL; char *cname = NULL; int nid; g_return_if_fail(tls != NULL); g_return_if_fail(ssl != NULL); if (SSL_get_server_tmp_key(ssl, &ephemeral_key)) { switch (EVP_PKEY_id(ephemeral_key)) { case EVP_PKEY_DH: tls_rec_set_ephemeral_key_algorithm(tls, "DH"); tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key)); break; #ifndef OPENSSL_NO_EC case EVP_PKEY_EC: ec_key = EVP_PKEY_get1_EC_KEY(ephemeral_key); nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)); EC_KEY_free(ec_key); cname = (char *)OBJ_nid2sn(nid); ephemeral_key_algorithm = g_strdup_printf("ECDH: %s", cname); tls_rec_set_ephemeral_key_algorithm(tls, ephemeral_key_algorithm); tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key)); g_free_and_null(ephemeral_key_algorithm); break; #endif default: tls_rec_set_ephemeral_key_algorithm(tls, "Unknown"); tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key)); break; } EVP_PKEY_free(ephemeral_key); } #endif /* SSL_get_server_tmp_key. */ }
ssize_t tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen) { SSL *conn = ctx->ssl_conn; const char *ocsp_pfx = "", *ocsp_info = ""; const char *proto = "-", *cipher = "-"; char dh[64]; int used_dh_bits = ctx->used_dh_bits, used_ecdh_nid = ctx->used_ecdh_nid; if (conn != NULL) { proto = SSL_get_version(conn); cipher = SSL_get_cipher(conn); #ifdef SSL_get_server_tmp_key if (ctx->flags & TLS_CLIENT) { EVP_PKEY *pk = NULL; int ok = SSL_get_server_tmp_key(conn, &pk); int pk_type = EVP_PKEY_id(pk); if (ok && pk) { if (pk_type == EVP_PKEY_DH) { DH *dh = EVP_PKEY_get0(pk); used_dh_bits = DH_size(dh) * 8; } else if (pk_type == EVP_PKEY_EC) { EC_KEY *ecdh = EVP_PKEY_get0(pk); const EC_GROUP *eg = EC_KEY_get0_group(ecdh); used_ecdh_nid = EC_GROUP_get_curve_name(eg); } EVP_PKEY_free(pk); } } #endif } if (used_dh_bits) { snprintf(dh, sizeof dh, "/DH=%d", used_dh_bits); } else if (used_ecdh_nid) { snprintf(dh, sizeof dh, "/ECDH=%s", OBJ_nid2sn(used_ecdh_nid)); } else { dh[0] = 0; } if (ctx->ocsp_result) { ocsp_info = ctx->ocsp_result; ocsp_pfx = "/OCSP="; } return snprintf(buf, buflen, "%s/%s%s%s%s", proto, cipher, dh, ocsp_pfx, ocsp_info); }
int ssl_print_tmp_key(BIO *out, SSL *s) { const char *cname; EVP_PKEY *pkey; EC_KEY *ec; int nid; if (!SSL_get_server_tmp_key(s, &pkey)) return 0; BIO_puts(out, "Server Temp Key: "); switch (EVP_PKEY_id(pkey)) { case EVP_PKEY_DH: BIO_printf(out, "DH, %d bits\n", EVP_PKEY_bits(pkey)); break; case EVP_PKEY_EC: ec = EVP_PKEY_get1_EC_KEY(pkey); nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); EC_KEY_free(ec); if ((cname = EC_curve_nid2nist(nid)) == NULL) cname = OBJ_nid2sn(nid); BIO_printf(out, "ECDH, %s, %d bits\n", cname, EVP_PKEY_bits(pkey)); break; default: BIO_printf(out, "%s, %d bits\n", OBJ_nid2sn(EVP_PKEY_id(pkey)), EVP_PKEY_bits(pkey)); } EVP_PKEY_free(pkey); return 1; }
/* * Note that |extra| points to the correct client/server configuration * within |test_ctx|. When configuring the handshake, general mode settings * are taken from |test_ctx|, and client/server-specific settings should be * taken from |extra|. * * The configuration code should never reach into |test_ctx->extra| or * |test_ctx->resume_extra| directly. * * (We could refactor test mode settings into a substructure. This would result * in cleaner argument passing but would complicate the test configuration * parsing.) */ static HANDSHAKE_RESULT *do_handshake_internal( SSL_CTX *server_ctx, SSL_CTX *server2_ctx, SSL_CTX *client_ctx, const SSL_TEST_CTX *test_ctx, const SSL_TEST_EXTRA_CONF *extra, SSL_SESSION *session_in, SSL_SESSION **session_out) { PEER server, client; BIO *client_to_server, *server_to_client; HANDSHAKE_EX_DATA server_ex_data, client_ex_data; CTX_DATA client_ctx_data, server_ctx_data, server2_ctx_data; HANDSHAKE_RESULT *ret = HANDSHAKE_RESULT_new(); int client_turn = 1, client_turn_count = 0; connect_phase_t phase = HANDSHAKE; handshake_status_t status = HANDSHAKE_RETRY; const unsigned char* tick = NULL; size_t tick_len = 0; SSL_SESSION* sess = NULL; const unsigned char *proto = NULL; /* API dictates unsigned int rather than size_t. */ unsigned int proto_len = 0; EVP_PKEY *tmp_key; memset(&server_ctx_data, 0, sizeof(server_ctx_data)); memset(&server2_ctx_data, 0, sizeof(server2_ctx_data)); memset(&client_ctx_data, 0, sizeof(client_ctx_data)); memset(&server, 0, sizeof(server)); memset(&client, 0, sizeof(client)); configure_handshake_ctx(server_ctx, server2_ctx, client_ctx, test_ctx, extra, &server_ctx_data, &server2_ctx_data, &client_ctx_data); /* Setup SSL and buffers; additional configuration happens below. */ create_peer(&server, server_ctx); create_peer(&client, client_ctx); server.bytes_to_write = client.bytes_to_read = test_ctx->app_data_size; client.bytes_to_write = server.bytes_to_read = test_ctx->app_data_size; configure_handshake_ssl(server.ssl, client.ssl, extra); if (session_in != NULL) { /* In case we're testing resumption without tickets. */ TEST_check(SSL_CTX_add_session(server_ctx, session_in)); TEST_check(SSL_set_session(client.ssl, session_in)); } memset(&server_ex_data, 0, sizeof(server_ex_data)); memset(&client_ex_data, 0, sizeof(client_ex_data)); ret->result = SSL_TEST_INTERNAL_ERROR; client_to_server = BIO_new(BIO_s_mem()); server_to_client = BIO_new(BIO_s_mem()); TEST_check(client_to_server != NULL); TEST_check(server_to_client != NULL); /* Non-blocking bio. */ BIO_set_nbio(client_to_server, 1); BIO_set_nbio(server_to_client, 1); SSL_set_connect_state(client.ssl); SSL_set_accept_state(server.ssl); /* The bios are now owned by the SSL object. */ SSL_set_bio(client.ssl, server_to_client, client_to_server); TEST_check(BIO_up_ref(server_to_client) > 0); TEST_check(BIO_up_ref(client_to_server) > 0); SSL_set_bio(server.ssl, client_to_server, server_to_client); ex_data_idx = SSL_get_ex_new_index(0, "ex data", NULL, NULL, NULL); TEST_check(ex_data_idx >= 0); TEST_check(SSL_set_ex_data(server.ssl, ex_data_idx, &server_ex_data) == 1); TEST_check(SSL_set_ex_data(client.ssl, ex_data_idx, &client_ex_data) == 1); SSL_set_info_callback(server.ssl, &info_cb); SSL_set_info_callback(client.ssl, &info_cb); client.status = server.status = PEER_RETRY; /* * Half-duplex handshake loop. * Client and server speak to each other synchronously in the same process. * We use non-blocking BIOs, so whenever one peer blocks for read, it * returns PEER_RETRY to indicate that it's the other peer's turn to write. * The handshake succeeds once both peers have succeeded. If one peer * errors out, we also let the other peer retry (and presumably fail). */ for(;;) { if (client_turn) { do_connect_step(test_ctx, &client, phase); status = handshake_status(client.status, server.status, 1 /* client went last */); } else { do_connect_step(test_ctx, &server, phase); status = handshake_status(server.status, client.status, 0 /* server went last */); } switch (status) { case HANDSHAKE_SUCCESS: client_turn_count = 0; phase = next_phase(test_ctx, phase); if (phase == CONNECTION_DONE) { ret->result = SSL_TEST_SUCCESS; goto err; } else { client.status = server.status = PEER_RETRY; /* * For now, client starts each phase. Since each phase is * started separately, we can later control this more * precisely, for example, to test client-initiated and * server-initiated shutdown. */ client_turn = 1; break; } case CLIENT_ERROR: ret->result = SSL_TEST_CLIENT_FAIL; goto err; case SERVER_ERROR: ret->result = SSL_TEST_SERVER_FAIL; goto err; case INTERNAL_ERROR: ret->result = SSL_TEST_INTERNAL_ERROR; goto err; case HANDSHAKE_RETRY: if (client_turn_count++ >= 2000) { /* * At this point, there's been so many PEER_RETRY in a row * that it's likely both sides are stuck waiting for a read. * It's time to give up. */ ret->result = SSL_TEST_INTERNAL_ERROR; goto err; } /* Continue. */ client_turn ^= 1; break; } } err: ret->server_alert_sent = server_ex_data.alert_sent; ret->server_num_fatal_alerts_sent = server_ex_data.num_fatal_alerts_sent; ret->server_alert_received = client_ex_data.alert_received; ret->client_alert_sent = client_ex_data.alert_sent; ret->client_num_fatal_alerts_sent = client_ex_data.num_fatal_alerts_sent; ret->client_alert_received = server_ex_data.alert_received; ret->server_protocol = SSL_version(server.ssl); ret->client_protocol = SSL_version(client.ssl); ret->servername = server_ex_data.servername; if ((sess = SSL_get0_session(client.ssl)) != NULL) SSL_SESSION_get0_ticket(sess, &tick, &tick_len); if (tick == NULL || tick_len == 0) ret->session_ticket = SSL_TEST_SESSION_TICKET_NO; else ret->session_ticket = SSL_TEST_SESSION_TICKET_YES; ret->session_ticket_do_not_call = server_ex_data.session_ticket_do_not_call; #ifndef OPENSSL_NO_NEXTPROTONEG SSL_get0_next_proto_negotiated(client.ssl, &proto, &proto_len); ret->client_npn_negotiated = dup_str(proto, proto_len); SSL_get0_next_proto_negotiated(server.ssl, &proto, &proto_len); ret->server_npn_negotiated = dup_str(proto, proto_len); #endif SSL_get0_alpn_selected(client.ssl, &proto, &proto_len); ret->client_alpn_negotiated = dup_str(proto, proto_len); SSL_get0_alpn_selected(server.ssl, &proto, &proto_len); ret->server_alpn_negotiated = dup_str(proto, proto_len); ret->client_resumed = SSL_session_reused(client.ssl); ret->server_resumed = SSL_session_reused(server.ssl); if (session_out != NULL) *session_out = SSL_get1_session(client.ssl); if (SSL_get_server_tmp_key(client.ssl, &tmp_key)) { ret->tmp_key_type = pkey_type(tmp_key); EVP_PKEY_free(tmp_key); } SSL_get_peer_signature_nid(client.ssl, &ret->server_sign_hash); SSL_get_peer_signature_nid(server.ssl, &ret->client_sign_hash); SSL_get_peer_signature_type_nid(client.ssl, &ret->server_sign_type); SSL_get_peer_signature_type_nid(server.ssl, &ret->client_sign_type); ret->server_cert_type = peer_pkey_type(client.ssl); ret->client_cert_type = peer_pkey_type(server.ssl); ctx_data_free_data(&server_ctx_data); ctx_data_free_data(&server2_ctx_data); ctx_data_free_data(&client_ctx_data); peer_free_data(&server); peer_free_data(&client); return ret; }