diff options
Diffstat (limited to 'src/ssl/test/bssl_shim.cc')
-rw-r--r-- | src/ssl/test/bssl_shim.cc | 717 |
1 files changed, 549 insertions, 168 deletions
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc index 40cb149..edae67b 100644 --- a/src/ssl/test/bssl_shim.cc +++ b/src/ssl/test/bssl_shim.cc @@ -38,10 +38,14 @@ #include <openssl/bio.h> #include <openssl/buf.h> #include <openssl/bytestring.h> +#include <openssl/cipher.h> #include <openssl/err.h> +#include <openssl/hmac.h> +#include <openssl/rand.h> #include <openssl/ssl.h> #include <memory> +#include <string> #include <vector> #include "../../crypto/test/scoped_types.h" @@ -90,10 +94,17 @@ struct TestState { ScopedSSL_SESSION pending_session; bool early_callback_called = false; bool handshake_done = false; + // private_key is the underlying private key used when testing custom keys. + ScopedEVP_PKEY private_key; + std::vector<uint8_t> signature; + // signature_retries is the number of times an asynchronous sign operation has + // been retried. + unsigned signature_retries = 0; + bool got_new_session = false; }; static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad, - int index, long argl, void *argp) { + int index, long argl, void *argp) { delete ((TestState *)ptr); } @@ -129,18 +140,137 @@ static ScopedEVP_PKEY LoadPrivateKey(const std::string &file) { return pkey; } +static int AsyncPrivateKeyType(SSL *ssl) { + return EVP_PKEY_id(GetTestState(ssl)->private_key.get()); +} + +static size_t AsyncPrivateKeyMaxSignatureLen(SSL *ssl) { + return EVP_PKEY_size(GetTestState(ssl)->private_key.get()); +} + +static ssl_private_key_result_t AsyncPrivateKeySign( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + const EVP_MD *md, const uint8_t *in, size_t in_len) { + TestState *test_state = GetTestState(ssl); + if (!test_state->signature.empty()) { + fprintf(stderr, "AsyncPrivateKeySign called with operation pending.\n"); + abort(); + } + + ScopedEVP_PKEY_CTX ctx(EVP_PKEY_CTX_new(test_state->private_key.get(), + nullptr)); + if (!ctx) { + return ssl_private_key_failure; + } + + // Write the signature into |test_state|. + size_t len = 0; + if (!EVP_PKEY_sign_init(ctx.get()) || + !EVP_PKEY_CTX_set_signature_md(ctx.get(), md) || + !EVP_PKEY_sign(ctx.get(), nullptr, &len, in, in_len)) { + return ssl_private_key_failure; + } + test_state->signature.resize(len); + if (!EVP_PKEY_sign(ctx.get(), bssl::vector_data(&test_state->signature), &len, + in, in_len)) { + return ssl_private_key_failure; + } + test_state->signature.resize(len); + + // The signature will be released asynchronously in |AsyncPrivateKeySignComplete|. + return ssl_private_key_retry; +} + +static ssl_private_key_result_t AsyncPrivateKeySignComplete( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out) { + TestState *test_state = GetTestState(ssl); + if (test_state->signature.empty()) { + fprintf(stderr, + "AsyncPrivateKeySignComplete called without operation pending.\n"); + abort(); + } + + if (test_state->signature_retries < 2) { + // Only return the signature on the second attempt, to test both incomplete + // |sign| and |sign_complete|. + return ssl_private_key_retry; + } + + if (max_out < test_state->signature.size()) { + fprintf(stderr, "Output buffer too small.\n"); + return ssl_private_key_failure; + } + memcpy(out, bssl::vector_data(&test_state->signature), + test_state->signature.size()); + *out_len = test_state->signature.size(); + + test_state->signature.clear(); + test_state->signature_retries = 0; + return ssl_private_key_success; +} + +static const SSL_PRIVATE_KEY_METHOD g_async_private_key_method = { + AsyncPrivateKeyType, + AsyncPrivateKeyMaxSignatureLen, + AsyncPrivateKeySign, + AsyncPrivateKeySignComplete, +}; + +template<typename T> +struct Free { + void operator()(T *buf) { + free(buf); + } +}; + static bool InstallCertificate(SSL *ssl) { const TestConfig *config = GetConfigPtr(ssl); - if (!config->key_file.empty() && - !SSL_use_PrivateKey_file(ssl, config->key_file.c_str(), - SSL_FILETYPE_PEM)) { - return false; + TestState *test_state = GetTestState(ssl); + + if (!config->digest_prefs.empty()) { + std::unique_ptr<char, Free<char>> digest_prefs( + strdup(config->digest_prefs.c_str())); + std::vector<int> digest_list; + + for (;;) { + char *token = + strtok(digest_list.empty() ? digest_prefs.get() : nullptr, ","); + if (token == nullptr) { + break; + } + + digest_list.push_back(EVP_MD_type(EVP_get_digestbyname(token))); + } + + if (!SSL_set_private_key_digest_prefs(ssl, digest_list.data(), + digest_list.size())) { + return false; + } + } + + if (!config->key_file.empty()) { + if (config->use_async_private_key) { + test_state->private_key = LoadPrivateKey(config->key_file.c_str()); + if (!test_state->private_key) { + return false; + } + SSL_set_private_key_method(ssl, &g_async_private_key_method); + } else if (!SSL_use_PrivateKey_file(ssl, config->key_file.c_str(), + SSL_FILETYPE_PEM)) { + return false; + } } if (!config->cert_file.empty() && !SSL_use_certificate_file(ssl, config->cert_file.c_str(), SSL_FILETYPE_PEM)) { return false; } + if (!config->ocsp_response.empty() && + !SSL_CTX_set_ocsp_response(ssl->ctx, + (const uint8_t *)config->ocsp_response.data(), + config->ocsp_response.size())) { + return false; + } return true; } @@ -196,10 +326,29 @@ static int SelectCertificateCallback(const struct ssl_early_callback_ctx *ctx) { return 1; } -static int SkipVerify(int preverify_ok, X509_STORE_CTX *store_ctx) { +static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) { + SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(store_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + const TestConfig *config = GetConfigPtr(ssl); + + if (!config->expected_ocsp_response.empty()) { + const uint8_t *data; + size_t len; + SSL_get0_ocsp_response(ssl, &data, &len); + if (len == 0) { + fprintf(stderr, "OCSP response not available in verify callback\n"); + return 0; + } + } + return 1; } +static int VerifyFail(X509_STORE_CTX *store_ctx, void *arg) { + store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; + return 0; +} + static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out, unsigned int *out_len, void *arg) { const TestConfig *config = GetConfigPtr(ssl); @@ -341,6 +490,94 @@ static void InfoCallback(const SSL *ssl, int type, int val) { } } +static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) { + GetTestState(ssl)->got_new_session = true; + // BoringSSL passes a reference to |session|. + SSL_SESSION_free(session); + return 1; +} + +static int TicketKeyCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv, + EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx, + int encrypt) { + // This is just test code, so use the all-zeros key. + static const uint8_t kZeros[16] = {0}; + + if (encrypt) { + memcpy(key_name, kZeros, sizeof(kZeros)); + RAND_bytes(iv, 16); + } else if (memcmp(key_name, kZeros, 16) != 0) { + return 0; + } + + if (!HMAC_Init_ex(hmac_ctx, kZeros, sizeof(kZeros), EVP_sha256(), NULL) || + !EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, kZeros, iv, encrypt)) { + return -1; + } + + if (!encrypt) { + return GetConfigPtr(ssl)->renew_ticket ? 2 : 1; + } + return 1; +} + +// kCustomExtensionValue is the extension value that the custom extension +// callbacks will add. +static const uint16_t kCustomExtensionValue = 1234; +static void *const kCustomExtensionAddArg = + reinterpret_cast<void *>(kCustomExtensionValue); +static void *const kCustomExtensionParseArg = + reinterpret_cast<void *>(kCustomExtensionValue + 1); +static const char kCustomExtensionContents[] = "custom extension"; + +static int CustomExtensionAddCallback(SSL *ssl, unsigned extension_value, + const uint8_t **out, size_t *out_len, + int *out_alert_value, void *add_arg) { + if (extension_value != kCustomExtensionValue || + add_arg != kCustomExtensionAddArg) { + abort(); + } + + if (GetConfigPtr(ssl)->custom_extension_skip) { + return 0; + } + if (GetConfigPtr(ssl)->custom_extension_fail_add) { + return -1; + } + + *out = reinterpret_cast<const uint8_t*>(kCustomExtensionContents); + *out_len = sizeof(kCustomExtensionContents) - 1; + + return 1; +} + +static void CustomExtensionFreeCallback(SSL *ssl, unsigned extension_value, + const uint8_t *out, void *add_arg) { + if (extension_value != kCustomExtensionValue || + add_arg != kCustomExtensionAddArg || + out != reinterpret_cast<const uint8_t *>(kCustomExtensionContents)) { + abort(); + } +} + +static int CustomExtensionParseCallback(SSL *ssl, unsigned extension_value, + const uint8_t *contents, + size_t contents_len, + int *out_alert_value, void *parse_arg) { + if (extension_value != kCustomExtensionValue || + parse_arg != kCustomExtensionParseArg) { + abort(); + } + + if (contents_len != sizeof(kCustomExtensionContents) - 1 || + memcmp(contents, kCustomExtensionContents, contents_len) != 0) { + *out_alert_value = SSL_AD_DECODE_ERROR; + return 0; + } + + return 1; +} + // Connect returns a new socket connected to localhost on |port| or -1 on // error. static int Connect(uint16_t port) { @@ -406,7 +643,23 @@ static ScopedSSL_CTX SetupCtx(const TestConfig *config) { return nullptr; } - if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL")) { + std::string cipher_list = "ALL"; + if (!config->cipher.empty()) { + cipher_list = config->cipher; + SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE); + } + if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), cipher_list.c_str())) { + return nullptr; + } + + if (!config->cipher_tls10.empty() && + !SSL_CTX_set_cipher_list_tls10(ssl_ctx.get(), + config->cipher_tls10.c_str())) { + return nullptr; + } + if (!config->cipher_tls11.empty() && + !SSL_CTX_set_cipher_list_tls11(ssl_ctx.get(), + config->cipher_tls11.c_str())) { return nullptr; } @@ -438,12 +691,46 @@ static ScopedSSL_CTX SetupCtx(const TestConfig *config) { SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL); } - ssl_ctx->tlsext_channel_id_enabled_new = 1; + SSL_CTX_enable_tls_channel_id(ssl_ctx.get()); SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback); ssl_ctx->current_time_cb = CurrentTimeCallback; SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback); + SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback); + + if (config->use_ticket_callback) { + SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx.get(), TicketKeyCallback); + } + + if (config->enable_client_custom_extension && + !SSL_CTX_add_client_custom_ext( + ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback, + CustomExtensionFreeCallback, kCustomExtensionAddArg, + CustomExtensionParseCallback, kCustomExtensionParseArg)) { + return nullptr; + } + + if (config->enable_server_custom_extension && + !SSL_CTX_add_server_custom_ext( + ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback, + CustomExtensionFreeCallback, kCustomExtensionAddArg, + CustomExtensionParseCallback, kCustomExtensionParseArg)) { + return nullptr; + } + + if (config->verify_fail) { + SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifyFail, NULL); + } else { + SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifySucceed, NULL); + } + + if (!config->signed_cert_timestamps.empty() && + !SSL_CTX_set_signed_cert_timestamp_list( + ssl_ctx.get(), (const uint8_t *)config->signed_cert_timestamps.data(), + config->signed_cert_timestamps.size())) { + return nullptr; + } return ssl_ctx; } @@ -500,6 +787,9 @@ static bool RetryAsync(SSL *ssl, int ret) { case SSL_ERROR_PENDING_CERTIFICATE: // The handshake will resume without a second call to the early callback. return InstallCertificate(ssl); + case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION: + test_state->signature_retries++; + return true; default: return false; } @@ -531,6 +821,177 @@ static int WriteAll(SSL *ssl, const uint8_t *in, size_t in_len) { return ret; } +// DoShutdown calls |SSL_shutdown|, resolving any asynchronous operations. It +// returns the result of the final |SSL_shutdown| call. +static int DoShutdown(SSL *ssl) { + const TestConfig *config = GetConfigPtr(ssl); + int ret; + do { + ret = SSL_shutdown(ssl); + } while (config->async && RetryAsync(ssl, ret)); + return ret; +} + +// CheckHandshakeProperties checks, immediately after |ssl| completes its +// initial handshake (or False Starts), whether all the properties are +// consistent with the test configuration and invariants. +static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) { + const TestConfig *config = GetConfigPtr(ssl); + + if (SSL_get_current_cipher(ssl) == nullptr) { + fprintf(stderr, "null cipher after handshake\n"); + return false; + } + + if (is_resume && + (!!SSL_session_reused(ssl) == config->expect_session_miss)) { + fprintf(stderr, "session was%s reused\n", + SSL_session_reused(ssl) ? "" : " not"); + return false; + } + + bool expect_handshake_done = is_resume || !config->false_start; + if (expect_handshake_done != GetTestState(ssl)->handshake_done) { + fprintf(stderr, "handshake was%s completed\n", + GetTestState(ssl)->handshake_done ? "" : " not"); + return false; + } + + if (expect_handshake_done && !config->is_server) { + bool expect_new_session = + !config->expect_no_session && + (!SSL_session_reused(ssl) || config->expect_ticket_renewal); + if (expect_new_session != GetTestState(ssl)->got_new_session) { + fprintf(stderr, + "new session was%s established, but we expected the opposite\n", + GetTestState(ssl)->got_new_session ? "" : " not"); + return false; + } + } + + if (config->is_server && !GetTestState(ssl)->early_callback_called) { + fprintf(stderr, "early callback not called\n"); + return false; + } + + if (!config->expected_server_name.empty()) { + const char *server_name = + SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name != config->expected_server_name) { + fprintf(stderr, "servername mismatch (got %s; want %s)\n", + server_name, config->expected_server_name.c_str()); + return false; + } + } + + if (!config->expected_certificate_types.empty()) { + const uint8_t *certificate_types; + size_t certificate_types_len = + SSL_get0_certificate_types(ssl, &certificate_types); + if (certificate_types_len != config->expected_certificate_types.size() || + memcmp(certificate_types, + config->expected_certificate_types.data(), + certificate_types_len) != 0) { + fprintf(stderr, "certificate types mismatch\n"); + return false; + } + } + + if (!config->expected_next_proto.empty()) { + const uint8_t *next_proto; + unsigned next_proto_len; + SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len); + if (next_proto_len != config->expected_next_proto.size() || + memcmp(next_proto, config->expected_next_proto.data(), + next_proto_len) != 0) { + fprintf(stderr, "negotiated next proto mismatch\n"); + return false; + } + } + + if (!config->expected_alpn.empty()) { + const uint8_t *alpn_proto; + unsigned alpn_proto_len; + SSL_get0_alpn_selected(ssl, &alpn_proto, &alpn_proto_len); + if (alpn_proto_len != config->expected_alpn.size() || + memcmp(alpn_proto, config->expected_alpn.data(), + alpn_proto_len) != 0) { + fprintf(stderr, "negotiated alpn proto mismatch\n"); + return false; + } + } + + if (!config->expected_channel_id.empty()) { + uint8_t channel_id[64]; + if (!SSL_get_tls_channel_id(ssl, channel_id, sizeof(channel_id))) { + fprintf(stderr, "no channel id negotiated\n"); + return false; + } + if (config->expected_channel_id.size() != 64 || + memcmp(config->expected_channel_id.data(), + channel_id, 64) != 0) { + fprintf(stderr, "channel id mismatch\n"); + return false; + } + } + + if (config->expect_extended_master_secret) { + if (!ssl->session->extended_master_secret) { + fprintf(stderr, "No EMS for session when expected"); + return false; + } + } + + if (!config->expected_ocsp_response.empty()) { + const uint8_t *data; + size_t len; + SSL_get0_ocsp_response(ssl, &data, &len); + if (config->expected_ocsp_response.size() != len || + memcmp(config->expected_ocsp_response.data(), data, len) != 0) { + fprintf(stderr, "OCSP response mismatch\n"); + return false; + } + } + + if (!config->expected_signed_cert_timestamps.empty()) { + const uint8_t *data; + size_t len; + SSL_get0_signed_cert_timestamp_list(ssl, &data, &len); + if (config->expected_signed_cert_timestamps.size() != len || + memcmp(config->expected_signed_cert_timestamps.data(), + data, len) != 0) { + fprintf(stderr, "SCT list mismatch\n"); + return false; + } + } + + if (config->expect_verify_result) { + int expected_verify_result = config->verify_fail ? + X509_V_ERR_APPLICATION_VERIFICATION : + X509_V_OK; + + if (SSL_get_verify_result(ssl) != expected_verify_result) { + fprintf(stderr, "Wrong certificate verification result\n"); + return false; + } + } + + if (!config->is_server) { + /* Clients should expect a peer certificate chain iff this was not a PSK + * cipher suite. */ + if (config->psk.empty()) { + if (SSL_get_peer_cert_chain(ssl) == nullptr) { + fprintf(stderr, "Missing peer certificate chain!\n"); + return false; + } + } else if (SSL_get_peer_cert_chain(ssl) != nullptr) { + fprintf(stderr, "Unexpected peer certificate chain!\n"); + return false; + } + } + return true; +} + // DoExchange runs a test SSL exchange against the peer. On success, it returns // true and sets |*out_session| to the negotiated SSL session. If the test is a // resumption attempt, |is_resume| is true and |session| is the session from the @@ -562,7 +1023,10 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, } if (config->require_any_client_certificate) { SSL_set_verify(ssl.get(), SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - SkipVerify); + NULL); + } + if (config->verify_peer) { + SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, NULL); } if (config->false_start) { SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START); @@ -588,8 +1052,8 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, if (config->tls_d5_bug) { SSL_set_options(ssl.get(), SSL_OP_TLS_D5_BUG); } - if (config->allow_unsafe_legacy_renegotiation) { - SSL_set_options(ssl.get(), SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); + if (config->microsoft_big_sslv3_buffer) { + SSL_set_options(ssl.get(), SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); } if (config->no_legacy_server_connect) { SSL_clear_options(ssl.get(), SSL_OP_LEGACY_SERVER_CONNECT); @@ -637,7 +1101,6 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, !SSL_enable_signed_cert_timestamps(ssl.get())) { return false; } - SSL_enable_fastradio_padding(ssl.get(), config->fastradio_padding); if (config->min_version != 0) { SSL_set_min_version(ssl.get(), (uint16_t)config->min_version); } @@ -651,14 +1114,13 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, if (config->install_ddos_callback) { SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback); } - if (!config->cipher.empty() && - !SSL_set_cipher_list(ssl.get(), config->cipher.c_str())) { - return false; - } if (!config->reject_peer_renegotiations) { /* Renegotiations are disabled by default. */ SSL_set_reject_peer_renegotiations(ssl.get(), 0); } + if (!config->check_close_notify) { + SSL_set_quiet_shutdown(ssl.get(), 1); + } int sock = Connect(config->port); if (sock == -1) { @@ -719,125 +1181,14 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, ret = SSL_connect(ssl.get()); } } while (config->async && RetryAsync(ssl.get(), ret)); - if (ret != 1) { - return false; - } - - if (SSL_get_current_cipher(ssl.get()) == nullptr) { - fprintf(stderr, "null cipher after handshake\n"); - return false; - } - - if (is_resume && - (!!SSL_session_reused(ssl.get()) == config->expect_session_miss)) { - fprintf(stderr, "session was%s reused\n", - SSL_session_reused(ssl.get()) ? "" : " not"); + if (ret != 1 || + !CheckHandshakeProperties(ssl.get(), is_resume)) { return false; } - bool expect_handshake_done = is_resume || !config->false_start; - if (expect_handshake_done != GetTestState(ssl.get())->handshake_done) { - fprintf(stderr, "handshake was%s completed\n", - GetTestState(ssl.get())->handshake_done ? "" : " not"); - return false; - } - - if (config->is_server && !GetTestState(ssl.get())->early_callback_called) { - fprintf(stderr, "early callback not called\n"); - return false; - } - - if (!config->expected_server_name.empty()) { - const char *server_name = - SSL_get_servername(ssl.get(), TLSEXT_NAMETYPE_host_name); - if (server_name != config->expected_server_name) { - fprintf(stderr, "servername mismatch (got %s; want %s)\n", - server_name, config->expected_server_name.c_str()); - return false; - } - } - - if (!config->expected_certificate_types.empty()) { - uint8_t *certificate_types; - int num_certificate_types = - SSL_get0_certificate_types(ssl.get(), &certificate_types); - if (num_certificate_types != - (int)config->expected_certificate_types.size() || - memcmp(certificate_types, - config->expected_certificate_types.data(), - num_certificate_types) != 0) { - fprintf(stderr, "certificate types mismatch\n"); - return false; - } - } - - if (!config->expected_next_proto.empty()) { - const uint8_t *next_proto; - unsigned next_proto_len; - SSL_get0_next_proto_negotiated(ssl.get(), &next_proto, &next_proto_len); - if (next_proto_len != config->expected_next_proto.size() || - memcmp(next_proto, config->expected_next_proto.data(), - next_proto_len) != 0) { - fprintf(stderr, "negotiated next proto mismatch\n"); - return false; - } - } - - if (!config->expected_alpn.empty()) { - const uint8_t *alpn_proto; - unsigned alpn_proto_len; - SSL_get0_alpn_selected(ssl.get(), &alpn_proto, &alpn_proto_len); - if (alpn_proto_len != config->expected_alpn.size() || - memcmp(alpn_proto, config->expected_alpn.data(), - alpn_proto_len) != 0) { - fprintf(stderr, "negotiated alpn proto mismatch\n"); - return false; - } - } - - if (!config->expected_channel_id.empty()) { - uint8_t channel_id[64]; - if (!SSL_get_tls_channel_id(ssl.get(), channel_id, sizeof(channel_id))) { - fprintf(stderr, "no channel id negotiated\n"); - return false; - } - if (config->expected_channel_id.size() != 64 || - memcmp(config->expected_channel_id.data(), - channel_id, 64) != 0) { - fprintf(stderr, "channel id mismatch\n"); - return false; - } - } - - if (config->expect_extended_master_secret) { - if (!ssl->session->extended_master_secret) { - fprintf(stderr, "No EMS for session when expected"); - return false; - } - } - - if (!config->expected_ocsp_response.empty()) { - const uint8_t *data; - size_t len; - SSL_get0_ocsp_response(ssl.get(), &data, &len); - if (config->expected_ocsp_response.size() != len || - memcmp(config->expected_ocsp_response.data(), data, len) != 0) { - fprintf(stderr, "OCSP response mismatch\n"); - return false; - } - } - - if (!config->expected_signed_cert_timestamps.empty()) { - const uint8_t *data; - size_t len; - SSL_get0_signed_cert_timestamp_list(ssl.get(), &data, &len); - if (config->expected_signed_cert_timestamps.size() != len || - memcmp(config->expected_signed_cert_timestamps.data(), - data, len) != 0) { - fprintf(stderr, "SCT list mismatch\n"); - return false; - } - } + // Reset the state to assert later that the callback isn't called in + // renegotations. + GetTestState(ssl.get())->got_new_session = false; } if (config->export_keying_material > 0) { @@ -883,18 +1234,19 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, } // This mode writes a number of different record sizes in an attempt to // trip up the CBC record splitting code. - uint8_t buf[32769]; - memset(buf, 0x42, sizeof(buf)); + static const size_t kBufLen = 32769; + std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufLen]); + memset(buf.get(), 0x42, kBufLen); static const size_t kRecordSizes[] = { 0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769}; for (size_t i = 0; i < sizeof(kRecordSizes) / sizeof(kRecordSizes[0]); i++) { const size_t len = kRecordSizes[i]; - if (len > sizeof(buf)) { + if (len > kBufLen) { fprintf(stderr, "Bad kRecordSizes value.\n"); return false; } - if (WriteAll(ssl.get(), buf, len) < 0) { + if (WriteAll(ssl.get(), buf.get(), len) < 0) { return false; } } @@ -905,53 +1257,82 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, return false; } } - for (;;) { - uint8_t buf[512]; - int n = DoRead(ssl.get(), buf, sizeof(buf)); - int err = SSL_get_error(ssl.get(), n); - if (err == SSL_ERROR_ZERO_RETURN || - (n == 0 && err == SSL_ERROR_SYSCALL)) { - if (n != 0) { - fprintf(stderr, "Invalid SSL_get_error output\n"); + if (!config->shim_shuts_down) { + for (;;) { + static const size_t kBufLen = 16384; + std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufLen]); + + // Read only 512 bytes at a time in TLS to ensure records may be + // returned in multiple reads. + int n = DoRead(ssl.get(), buf.get(), config->is_dtls ? kBufLen : 512); + int err = SSL_get_error(ssl.get(), n); + if (err == SSL_ERROR_ZERO_RETURN || + (n == 0 && err == SSL_ERROR_SYSCALL)) { + if (n != 0) { + fprintf(stderr, "Invalid SSL_get_error output\n"); + return false; + } + // Stop on either clean or unclean shutdown. + break; + } else if (err != SSL_ERROR_NONE) { + if (n > 0) { + fprintf(stderr, "Invalid SSL_get_error output\n"); + return false; + } return false; } - // Accept shutdowns with or without close_notify. - // TODO(davidben): Write tests which distinguish these two cases. - break; - } else if (err != SSL_ERROR_NONE) { - if (n > 0) { + // Successfully read data. + if (n <= 0) { fprintf(stderr, "Invalid SSL_get_error output\n"); return false; } - return false; - } - // Successfully read data. - if (n <= 0) { - fprintf(stderr, "Invalid SSL_get_error output\n"); - return false; - } - // After a successful read, with or without False Start, the handshake - // must be complete. - if (!GetTestState(ssl.get())->handshake_done) { - fprintf(stderr, "handshake was not completed after SSL_read\n"); - return false; - } + // After a successful read, with or without False Start, the handshake + // must be complete. + if (!GetTestState(ssl.get())->handshake_done) { + fprintf(stderr, "handshake was not completed after SSL_read\n"); + return false; + } - for (int i = 0; i < n; i++) { - buf[i] ^= 0xff; - } - if (WriteAll(ssl.get(), buf, n) < 0) { - return false; + for (int i = 0; i < n; i++) { + buf[i] ^= 0xff; + } + if (WriteAll(ssl.get(), buf.get(), n) < 0) { + return false; + } } } } + if (!config->is_server && !config->false_start && + !config->implicit_handshake && + GetTestState(ssl.get())->got_new_session) { + fprintf(stderr, "new session was established after the handshake\n"); + return false; + } + if (out_session) { out_session->reset(SSL_get1_session(ssl.get())); } - SSL_shutdown(ssl.get()); + ret = DoShutdown(ssl.get()); + + if (config->shim_shuts_down && config->check_close_notify) { + // We initiate shutdown, so |SSL_shutdown| will return in two stages. First + // it returns zero when our close_notify is sent, then one when the peer's + // is received. + if (ret != 0) { + fprintf(stderr, "Unexpected SSL_shutdown result: %d != 0\n", ret); + return false; + } + ret = DoShutdown(ssl.get()); + } + + if (ret != 1) { + fprintf(stderr, "Unexpected SSL_shutdown result: %d != 1\n", ret); + return false; + } + return true; } |