diff options
author | David Benjamin <davidben@chromium.org> | 2014-09-02 16:29:36 -0400 |
---|---|---|
committer | Adam Langley <agl@google.com> | 2014-09-03 20:15:55 +0000 |
commit | 859ec3cc09f244348f3c919693817acb01064535 (patch) | |
tree | 4a962515b803f19caacf66f8303c15db1476ea47 | |
parent | 60d612fdcf7052008fcc17a503854eac1653c739 (diff) | |
download | src-859ec3cc09f244348f3c919693817acb01064535.tar.gz |
Add SSL_CTX_set_keylog_bio.
Configures the SSL stack to log session information to a BIO. The intent is to
support NSS's SSLKEYLOGFILE environment variable. Add support for the same
environment variable to tool/client.cc.
Tested against Wireshark 1.12.0.
BUG=393477
Change-Id: I4c231f9abebf194eb2df4aaeeafa337516774c95
Reviewed-on: https://boringssl-review.googlesource.com/1699
Reviewed-by: Adam Langley <agl@google.com>
-rw-r--r-- | include/openssl/ssl.h | 15 | ||||
-rw-r--r-- | ssl/s3_both.c | 8 | ||||
-rw-r--r-- | ssl/s3_clnt.c | 7 | ||||
-rw-r--r-- | ssl/ssl_error.c | 2 | ||||
-rw-r--r-- | ssl/ssl_lib.c | 119 | ||||
-rw-r--r-- | ssl/ssl_locl.h | 13 | ||||
-rw-r--r-- | tool/client.cc | 11 |
7 files changed, 175 insertions, 0 deletions
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 900fae7..da8e55e 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -689,6 +689,14 @@ OPENSSL_EXPORT void SSL_set_msg_callback(SSL *ssl, void (*cb)(int write_p, int v #define SSL_CTX_set_msg_callback_arg(ctx, arg) SSL_CTX_ctrl((ctx), SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, (arg)) #define SSL_set_msg_callback_arg(ssl, arg) SSL_ctrl((ssl), SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, (arg)) +/* SSL_CTX_set_keylog_bio sets configures all SSL objects attached to |ctx| to + * log session material to |keylog_bio|. This is intended for debugging use with + * tools like Wireshark. |ctx| takes ownership of |keylog_bio|. + * + * The format is described in + * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. */ +void SSL_CTX_set_keylog_bio(SSL_CTX *ctx, BIO *keylog_bio); + struct ssl_aead_ctx_st; typedef struct ssl_aead_ctx_st SSL_AEAD_CTX; @@ -1033,6 +1041,11 @@ struct ssl_ctx_st /* If true, a client will request a stapled OCSP response. */ char ocsp_stapling_enabled; + + /* If not NULL, session key material will be logged to this BIO for + * debugging purposes. The format matches NSS's and is readable by + * Wireshark. */ + BIO *keylog_bio; }; #endif @@ -2474,6 +2487,8 @@ OPENSSL_EXPORT void ERR_load_SSL_strings(void); #define SSL_F_ssl3_expect_change_cipher_spec 282 #define SSL_F_ssl23_get_v2_client_hello 283 #define SSL_F_ssl3_cert_verify_hash 284 +#define SSL_F_ssl_ctx_log_rsa_client_key_exchange 285 +#define SSL_F_ssl_ctx_log_master_secret 286 #define SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS 100 #define SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC 101 #define SSL_R_INVALID_NULL_CMD_NAME 102 diff --git a/ssl/s3_both.c b/ssl/s3_both.c index f5bd284..c494a4a 100644 --- a/ssl/s3_both.c +++ b/ssl/s3_both.c @@ -168,6 +168,14 @@ int ssl3_send_finished(SSL *s, int a, int b, const char *sender, int slen) memcpy(p, s->s3->tmp.finish_md, i); l=i; + /* Log the master secret, if logging is enabled. */ + if (!ssl_ctx_log_master_secret(s->ctx, + s->s3->client_random, SSL3_RANDOM_SIZE, + s->session->master_key, s->session->master_key_length)) + { + return 0; + } + /* Copy the finished so we can use it for renegotiation checks */ if(s->type == SSL_ST_CONNECT) diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index 5f8c5db..cb9f95f 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -2015,6 +2015,13 @@ int ssl3_send_client_key_exchange(SSL *s) goto err; } + /* Log the premaster secret, if logging is enabled. */ + if (!ssl_ctx_log_rsa_client_key_exchange(s->ctx, + p, n, tmp_buf, sizeof(tmp_buf))) + { + goto err; + } + /* Fix buf for TLS and beyond */ if (s->version > SSL3_VERSION) { diff --git a/ssl/ssl_error.c b/ssl/ssl_error.c index 20737a5..f6120dc 100644 --- a/ssl/ssl_error.c +++ b/ssl/ssl_error.c @@ -163,6 +163,8 @@ const ERR_STRING_DATA SSL_error_string_data[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cipher_process_rulestr, 0), "ssl_cipher_process_rulestr"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cipher_strength_sort, 0), "ssl_cipher_strength_sort"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_create_cipher_list, 0), "ssl_create_cipher_list"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_ctx_log_master_secret, 0), "ssl_ctx_log_master_secret"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_ctx_log_rsa_client_key_exchange, 0), "ssl_ctx_log_rsa_client_key_exchange"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_ctx_make_profiles, 0), "ssl_ctx_make_profiles"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_get_new_session, 0), "ssl_get_new_session"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_get_prev_session, 0), "ssl_get_prev_session"}, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index b8df1bf..1ad825a 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -2130,6 +2130,9 @@ void SSL_CTX_free(SSL_CTX *a) if (a->tlsext_channel_id_private) EVP_PKEY_free(a->tlsext_channel_id_private); + if (a->keylog_bio) + BIO_free(a->keylog_bio); + OPENSSL_free(a); } @@ -3088,6 +3091,122 @@ void SSL_set_msg_callback(SSL *ssl, void (*cb)(int write_p, int version, int con SSL_callback_ctrl(ssl, SSL_CTRL_SET_MSG_CALLBACK, (void (*)(void))cb); } +void SSL_CTX_set_keylog_bio(SSL_CTX *ctx, BIO *keylog_bio) + { + if (ctx->keylog_bio != NULL) + BIO_free(ctx->keylog_bio); + ctx->keylog_bio = keylog_bio; + } + +static int cbb_add_hex(CBB *cbb, const uint8_t *in, size_t in_len) + { + static const char hextable[] = "0123456789abcdef"; + uint8_t *out; + size_t i; + + if (!CBB_add_space(cbb, &out, in_len * 2)) + { + return 0; + } + + for (i = 0; i < in_len; i++) + { + *(out++) = (uint8_t)hextable[in[i] >> 4]; + *(out++) = (uint8_t)hextable[in[i] & 0xf]; + } + return 1; + } + +int ssl_ctx_log_rsa_client_key_exchange(SSL_CTX *ctx, + const uint8_t *encrypted_premaster, size_t encrypted_premaster_len, + const uint8_t *premaster, size_t premaster_len) + { + BIO *bio = ctx->keylog_bio; + CBB cbb; + uint8_t *out; + size_t out_len; + int ret; + + if (bio == NULL) + { + return 1; + } + + if (encrypted_premaster_len < 8) + { + OPENSSL_PUT_ERROR(SSL, ssl_ctx_log_rsa_client_key_exchange, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (!CBB_init(&cbb, 4 + 16 + 1 + premaster_len*2 + 1)) + { + return 0; + } + if (!CBB_add_bytes(&cbb, (const uint8_t*)"RSA ", 4) || + /* Only the first 8 bytes of the encrypted premaster secret are + * logged. */ + !cbb_add_hex(&cbb, encrypted_premaster, 8) || + !CBB_add_bytes(&cbb, (const uint8_t*)" ", 1) || + !cbb_add_hex(&cbb, premaster, premaster_len) || + !CBB_add_bytes(&cbb, (const uint8_t*)"\n", 1) || + !CBB_finish(&cbb, &out, &out_len)) + { + CBB_cleanup(&cbb); + return 0; + } + + CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); + ret = BIO_write(bio, out, out_len) >= 0 && BIO_flush(bio); + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); + + OPENSSL_free(out); + return ret; + } + +int ssl_ctx_log_master_secret(SSL_CTX *ctx, + const uint8_t *client_random, size_t client_random_len, + const uint8_t *master, size_t master_len) + { + BIO *bio = ctx->keylog_bio; + CBB cbb; + uint8_t *out; + size_t out_len; + int ret; + + if (bio == NULL) + { + return 1; + } + + if (client_random_len != 32) + { + OPENSSL_PUT_ERROR(SSL, ssl_ctx_log_master_secret, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (!CBB_init(&cbb, 14 + 64 + 1 + master_len*2 + 1)) + { + return 0; + } + if (!CBB_add_bytes(&cbb, (const uint8_t*)"CLIENT_RANDOM ", 14) || + !cbb_add_hex(&cbb, client_random, 32) || + !CBB_add_bytes(&cbb, (const uint8_t*)" ", 1) || + !cbb_add_hex(&cbb, master, master_len) || + !CBB_add_bytes(&cbb, (const uint8_t*)"\n", 1) || + !CBB_finish(&cbb, &out, &out_len)) + { + CBB_cleanup(&cbb); + return 0; + } + + CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); + ret = BIO_write(bio, out, out_len) >= 0 && BIO_flush(bio); + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); + + OPENSSL_free(out); + return ret; + } + int SSL_cutthrough_complete(const SSL *s) { return (!s->server && /* cutthrough only applies to clients */ diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index ad2d843..2d10650 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1091,6 +1091,19 @@ int tls1_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain, int idx); void tls1_set_cert_validity(SSL *s); +/* ssl_ctx_log_rsa_client_key_exchange logs |premaster| to |ctx|, if logging is + * enabled. It returns one on success and zero on failure. The entry is + * identified by the first 8 bytes of |encrypted_premaster|. */ +int ssl_ctx_log_rsa_client_key_exchange(SSL_CTX *ctx, + const uint8_t *encrypted_premaster, size_t encrypted_premaster_len, + const uint8_t *premaster, size_t premaster_len); + +/* ssl_ctx_log_master_secret logs |master| to |ctx|, if logging is enabled. It + * returns one on success and zero on failure. The entry is identified by + * |client_random|. */ +int ssl_ctx_log_master_secret(SSL_CTX *ctx, + const uint8_t *client_random, size_t client_random_len, + const uint8_t *master, size_t master_len); int ssl3_can_cutthrough(const SSL *s); int ssl_get_max_version(const SSL *s); diff --git a/tool/client.cc b/tool/client.cc index 5acc8b1..8aef559 100644 --- a/tool/client.cc +++ b/tool/client.cc @@ -18,6 +18,7 @@ #include <vector> #include <errno.h> +#include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> @@ -253,6 +254,16 @@ bool Client(const std::vector<std::string> &args) { SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method()); + const char *keylog_file = getenv("SSLKEYLOGFILE"); + if (keylog_file) { + BIO *keylog_bio = BIO_new_file(keylog_file, "a"); + if (!keylog_bio) { + ERR_print_errors_cb(PrintErrorCallback, stderr); + return false; + } + SSL_CTX_set_keylog_bio(ctx, keylog_bio); + } + int sock = -1; if (!Connect(&sock, args_map["-connect"])) { return false; |