From 56d250321ea9dfa66ea9afa599f12c83a4147c86 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Tue, 23 Jun 2015 16:20:13 -0700 Subject: Fixes for CVE-2015-1791. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a NewSessionTicket is received by a multi-threaded client when attempting to reuse a previous ticket then a race condition can occur potentially leading to a double free of the ticket data. This change cherry-picks the following BoringSSL changes: b31040d0 – Get rid of CERT_PKEY slots in SESS_CERT. fd67aa8c – Add SSL_SESSION_from_bytes. 95d31825 – Duplicate SSL_SESSIONs when renewing them. d65bb78c – Add SSL_initial_handshake_complete. 680ca961 – Preserve session->sess_cert on ticket renewal. Change-Id: I474065330842e4ab0066b2485c1489a50e4dfd5b --- err_data.c | 550 ++++++++++++++++++++++--------------------- src/crypto/err/ssl.errordata | 7 +- src/include/openssl/ssl.h | 45 ++-- src/ssl/internal.h | 18 +- src/ssl/s3_clnt.c | 74 +++--- src/ssl/ssl_asn1.c | 129 ++++++---- src/ssl/ssl_cert.c | 51 ++-- src/ssl/ssl_lib.c | 14 +- src/ssl/ssl_test.cc | 35 ++- src/ssl/t1_lib.c | 2 +- src/ssl/test/bssl_shim.cc | 14 ++ 11 files changed, 536 insertions(+), 403 deletions(-) diff --git a/err_data.c b/err_data.c index 5a79137..f884a72 100644 --- a/err_data.c +++ b/err_data.c @@ -218,65 +218,65 @@ const uint32_t kOpenSSLFunctionValues[] = { 0x28330ac8, 0x28338acd, 0x28340ad8, - 0x2c322b00, - 0x2c32ab0c, - 0x2c332b1f, - 0x2c33ab30, - 0x2c342b49, - 0x2c34ab71, - 0x2c352b88, - 0x2c35aba5, - 0x2c362bc2, - 0x2c36abdf, - 0x2c372bf8, - 0x2c37ac11, - 0x2c382c27, - 0x2c38ac35, - 0x2c392c47, - 0x2c39ac64, - 0x2c3a2c81, - 0x2c3aac8f, - 0x2c3b2cad, - 0x2c3baccb, - 0x2c3c2ce6, - 0x2c3cacfa, - 0x2c3d2d0c, - 0x2c3dad1c, - 0x2c3e2d2a, - 0x2c3ead3a, - 0x2c3f2d4a, - 0x2c3fad65, - 0x2c402d76, - 0x2c40ad91, - 0x2c412da5, - 0x2c41adb8, - 0x2c422dd7, - 0x2c42adeb, - 0x2c432dfe, - 0x2c43ae0d, - 0x2c442e1c, - 0x2c44ae33, - 0x2c452e4e, - 0x2c45ae66, - 0x2c462e7a, - 0x2c46ae8d, - 0x2c472e9e, - 0x2c47aeaf, - 0x2c482ec0, - 0x2c48aed1, - 0x2c492ee0, - 0x2c49aeed, - 0x2c4a2efa, - 0x2c4aaf07, - 0x2c4b2f10, - 0x2c4baf24, - 0x2c4c2f33, - 0x2c4caf41, - 0x2c4d2f63, - 0x2c4daf74, - 0x2c4e2f85, - 0x2c4eaf50, - 0x2c4f2b62, + 0x2c322b47, + 0x2c32ab53, + 0x2c332b66, + 0x2c33ab77, + 0x2c342b90, + 0x2c34abb8, + 0x2c352bcf, + 0x2c35abec, + 0x2c362c09, + 0x2c36ac26, + 0x2c372c3f, + 0x2c37ac58, + 0x2c382c6e, + 0x2c38ac7c, + 0x2c392c8e, + 0x2c39acab, + 0x2c3a2cc8, + 0x2c3aacd6, + 0x2c3b2cf4, + 0x2c3bad12, + 0x2c3c2d2d, + 0x2c3cad41, + 0x2c3d2d53, + 0x2c3dad63, + 0x2c3e2d71, + 0x2c3ead81, + 0x2c3f2d91, + 0x2c3fadac, + 0x2c402dbd, + 0x2c40add8, + 0x2c412dec, + 0x2c41adff, + 0x2c422e1e, + 0x2c42ae32, + 0x2c432e45, + 0x2c43ae54, + 0x2c442e63, + 0x2c44ae7a, + 0x2c452e95, + 0x2c45aead, + 0x2c462ec1, + 0x2c46aed4, + 0x2c472ee5, + 0x2c47aef6, + 0x2c482f07, + 0x2c48af18, + 0x2c492f27, + 0x2c49af34, + 0x2c4a2f41, + 0x2c4aaf4e, + 0x2c4b2f57, + 0x2c4baf6b, + 0x2c4c2f7a, + 0x2c4caf88, + 0x2c4d2faa, + 0x2c4dafbb, + 0x2c4e2fcc, + 0x2c4eaf97, + 0x2c4f2ba9, 0x30320000, 0x30328018, 0x3033002c, @@ -432,166 +432,169 @@ const uint32_t kOpenSSLFunctionValues[] = { 0x40389d40, 0x40391d63, 0x40399d80, - 0x403a1d9e, - 0x403a9dae, - 0x403b1dc3, - 0x403b9ddf, - 0x403c1df9, - 0x403c9e04, - 0x403d1e27, - 0x403d9e4b, - 0x403e1e61, - 0x403e9e6b, - 0x403f1e77, - 0x403f9e88, - 0x40401ea0, - 0x40409ea8, - 0x40411eb1, - 0x40419eba, - 0x40421ee2, - 0x40429ef6, - 0x40431f01, - 0x40439f0d, - 0x40441f61, - 0x40449f6d, - 0x40451f7a, - 0x40459f8d, - 0x40461fa5, - 0x40469fbd, - 0x40471fd3, - 0x40479fee, - 0x40482009, - 0x4048a01d, - 0x40492036, - 0x4049a04f, - 0x404a2069, - 0x404aa073, - 0x404b2083, - 0x404ba0a4, - 0x404c20bf, - 0x404ca0cd, - 0x404d20da, - 0x404da0ee, - 0x404e2106, - 0x404ea114, - 0x404f213e, - 0x404fa155, - 0x40502167, - 0x4050a198, - 0x405121c9, - 0x4051a1de, - 0x40522201, - 0x4052a221, - 0x40532236, - 0x4053a246, - 0x4054a252, - 0x40552268, - 0x4055a286, - 0x40562293, - 0x4056a29d, - 0x405722ab, - 0x4057a2c6, - 0x405822e1, - 0x4058a300, - 0x40592315, - 0x4059a32a, - 0x405a2347, - 0x405aa35b, - 0x405b2377, - 0x405ba38d, - 0x405c23aa, - 0x405ca3bc, - 0x405d23d3, - 0x405da3e4, - 0x405e2400, - 0x405ea414, - 0x405f2424, - 0x405fa440, - 0x40602455, - 0x4060a46b, - 0x40612488, - 0x4061a4a1, - 0x406224cb, - 0x4062a4d4, - 0x406324e4, - 0x4063a51d, - 0x40642533, - 0x4064a551, - 0x40652566, - 0x4065a583, - 0x4066259a, - 0x4066a5b8, - 0x406725d5, - 0x4067a5ec, - 0x4068260a, - 0x4068a621, - 0x40692639, - 0x4069a64a, - 0x406a265d, - 0x406aa670, - 0x406b2684, - 0x406ba6a8, - 0x406c26c3, - 0x406ca6e4, - 0x406d2708, - 0x406da723, - 0x406e2744, - 0x406ea759, - 0x406f2772, - 0x406fa77f, - 0x4070278d, - 0x4070a79a, - 0x407127b7, - 0x4071a7d7, - 0x407227f2, - 0x4072a80b, - 0x40732822, - 0x4073a83c, - 0x40742860, - 0x4074a876, - 0x4075288a, - 0x4075a89f, - 0x407628b9, - 0x4076a8cb, - 0x407728e0, - 0x4077a906, - 0x40782923, - 0x4078a946, - 0x4079296c, - 0x4079a989, - 0x407a29ac, - 0x407aa9c8, - 0x407b29e4, - 0x407ba9f6, - 0x407c2a03, - 0x407e2a10, - 0x407eaa26, - 0x407f2a3e, - 0x407faa51, - 0x40802a66, - 0x4080aa7f, - 0x40812a9d, - 0x4081aabd, - 0x40822ac6, - 0x4082aae2, - 0x40832aeb, - 0x4083a123, - 0x408421b2, - 0x4084a182, - 0x4085250c, - 0x4085a4f0, + 0x403a1db5, + 0x403a9e0f, + 0x403b1e24, + 0x403b9e40, + 0x403c1e5a, + 0x403c9e65, + 0x403d1e88, + 0x403d9eac, + 0x403e1ec2, + 0x403e9ecc, + 0x403f1ed8, + 0x403f9ee9, + 0x40401f01, + 0x40409f09, + 0x40411f12, + 0x40419f1b, + 0x40421f43, + 0x40429f57, + 0x40431f62, + 0x40439f6e, + 0x40441fc2, + 0x40449fce, + 0x40451fdb, + 0x40459fee, + 0x40462006, + 0x4046a01e, + 0x40472034, + 0x4047a04f, + 0x4048206a, + 0x4048a07e, + 0x40492097, + 0x4049a0b0, + 0x404a20ca, + 0x404aa0d4, + 0x404b1dd7, + 0x404b9df6, + 0x404c20e4, + 0x404ca0f2, + 0x404d20ff, + 0x404da113, + 0x404e212b, + 0x404ea139, + 0x404f2163, + 0x404fa17a, + 0x4050218c, + 0x4050a1bd, + 0x405121ee, + 0x4051a203, + 0x40522226, + 0x4052a246, + 0x4053225b, + 0x4053a26b, + 0x4054a277, + 0x4055228d, + 0x4055a2cd, + 0x405622da, + 0x4056a2e4, + 0x405722f2, + 0x4057a30d, + 0x40582328, + 0x4058a347, + 0x4059235c, + 0x4059a371, + 0x405a238e, + 0x405aa3a2, + 0x405b23be, + 0x405ba3d4, + 0x405c23f1, + 0x405ca403, + 0x405d241a, + 0x405da42b, + 0x405e2447, + 0x405ea45b, + 0x405f246b, + 0x405fa487, + 0x4060249c, + 0x4060a4b2, + 0x406124cf, + 0x4061a4e8, + 0x40622512, + 0x4062a51b, + 0x4063252b, + 0x4063a564, + 0x4064257a, + 0x4064a598, + 0x406525ad, + 0x4065a5ca, + 0x406625e1, + 0x4066a5ff, + 0x4067261c, + 0x4067a633, + 0x40682651, + 0x4068a668, + 0x40692680, + 0x4069a691, + 0x406a26a4, + 0x406aa6b7, + 0x406b26cb, + 0x406ba6ef, + 0x406c270a, + 0x406ca72b, + 0x406d274f, + 0x406da76a, + 0x406e278b, + 0x406ea7a0, + 0x406f27b9, + 0x406fa7c6, + 0x407027d4, + 0x4070a7e1, + 0x407127fe, + 0x4071a81e, + 0x40722839, + 0x4072a852, + 0x40732869, + 0x4073a883, + 0x407428a7, + 0x4074a8bd, + 0x407528d1, + 0x4075a8e6, + 0x40762900, + 0x4076a912, + 0x40772927, + 0x4077a94d, + 0x4078296a, + 0x4078a98d, + 0x407929b3, + 0x4079a9d0, + 0x407a29f3, + 0x407aaa0f, + 0x407b2a2b, + 0x407baa3d, + 0x407c2a4a, + 0x407e2a57, + 0x407eaa6d, + 0x407f2a85, + 0x407faa98, + 0x40802aad, + 0x4080aac6, + 0x40812ae4, + 0x4081ab04, + 0x40822b0d, + 0x4082ab29, + 0x40832b32, + 0x4083a148, + 0x408421d7, + 0x4084a1a7, + 0x40852553, + 0x4085a537, 0x40861c3c, 0x40869c4f, - 0x40871f41, - 0x40879f50, + 0x40871fa2, + 0x40879fb1, 0x40881bcb, - 0x40889eca, - 0x40891f28, - 0x4089a4b4, + 0x40889f2b, + 0x40891f89, + 0x4089a4fb, 0x408a1b70, 0x408a9b81, 0x408b1b93, - 0x408ba1ef, + 0x408ba214, + 0x408c1d9e, + 0x408c9dc5, + 0x408d22ab, 0x4432042a, 0x4432843c, 0x44330445, @@ -629,63 +632,63 @@ const uint32_t kOpenSSLFunctionValues[] = { 0x4c3998fe, 0x4c3a1916, 0x4c3a9929, - 0x50322f96, - 0x5032afab, - 0x50332fbc, - 0x5033afcf, - 0x50342fe0, - 0x5034aff3, - 0x50353002, - 0x5035b017, - 0x50363027, - 0x5036b036, - 0x50373047, - 0x5037b057, - 0x50383068, - 0x5038b07b, - 0x5039308d, - 0x5039b0a3, - 0x503a30b5, - 0x503ab0c6, - 0x503b30d7, - 0x503bb0e8, - 0x503c30f3, - 0x503cb0ff, - 0x503d310a, - 0x503db115, - 0x503e3122, - 0x503eb137, - 0x503f3145, - 0x503fb159, - 0x5040316c, - 0x5040b17d, - 0x50413197, - 0x5041b1a6, - 0x504231af, - 0x5042b1be, - 0x504331d0, - 0x5043b1dc, - 0x504431e4, - 0x5044b1f7, - 0x50453208, - 0x5045b21e, - 0x5046322a, - 0x5046b23e, - 0x5047324c, - 0x5047b260, - 0x5048327a, - 0x5048b28e, - 0x504932a4, - 0x5049b2bb, - 0x504a32cd, - 0x504ab2e1, - 0x504b32f6, - 0x504bb30d, - 0x504c3321, - 0x504cb32a, - 0x504d3332, - 0x504db341, - 0x504e3351, + 0x50322fdd, + 0x5032aff2, + 0x50333003, + 0x5033b016, + 0x50343027, + 0x5034b03a, + 0x50353049, + 0x5035b05e, + 0x5036306e, + 0x5036b07d, + 0x5037308e, + 0x5037b09e, + 0x503830af, + 0x5038b0c2, + 0x503930d4, + 0x5039b0ea, + 0x503a30fc, + 0x503ab10d, + 0x503b311e, + 0x503bb12f, + 0x503c313a, + 0x503cb146, + 0x503d3151, + 0x503db15c, + 0x503e3169, + 0x503eb17e, + 0x503f318c, + 0x503fb1a0, + 0x504031b3, + 0x5040b1c4, + 0x504131de, + 0x5041b1ed, + 0x504231f6, + 0x5042b205, + 0x50433217, + 0x5043b223, + 0x5044322b, + 0x5044b23e, + 0x5045324f, + 0x5045b265, + 0x50463271, + 0x5046b285, + 0x50473293, + 0x5047b2a7, + 0x504832c1, + 0x5048b2d5, + 0x504932eb, + 0x5049b302, + 0x504a3314, + 0x504ab328, + 0x504b333d, + 0x504bb354, + 0x504c3368, + 0x504cb371, + 0x504d3379, + 0x504db388, + 0x504e3398, 0x68321109, 0x6832911a, 0x6833112a, @@ -1141,7 +1144,11 @@ const char kOpenSSLFunctionStringData[] = "SSL_CTX_use_certificate_chain_file\0" "SSL_CTX_use_certificate_file\0" "SSL_CTX_use_psk_identity_hint\0" + "SSL_SESSION_from_bytes\0" "SSL_SESSION_new\0" + "SSL_SESSION_parse\0" + "SSL_SESSION_parse_octet_string\0" + "SSL_SESSION_parse_string\0" "SSL_SESSION_print_fp\0" "SSL_SESSION_set1_id_context\0" "SSL_SESSION_to_bytes_full\0" @@ -1179,8 +1186,6 @@ const char kOpenSSLFunctionStringData[] = "SSL_use_psk_identity_hint\0" "SSL_write\0" "d2i_SSL_SESSION\0" - "d2i_SSL_SESSION_get_octet_string\0" - "d2i_SSL_SESSION_get_string\0" "do_ssl3_write\0" "dtls1_accept\0" "dtls1_buffer_record\0" @@ -1203,6 +1208,7 @@ const char kOpenSSLFunctionStringData[] = "ssl3_accept\0" "ssl3_cert_verify_hash\0" "ssl3_check_cert_and_algorithm\0" + "ssl3_check_certificate_for_cipher\0" "ssl3_connect\0" "ssl3_ctrl\0" "ssl3_ctx_ctrl\0" diff --git a/src/crypto/err/ssl.errordata b/src/crypto/err/ssl.errordata index 4ae0a51..9464c3d 100644 --- a/src/crypto/err/ssl.errordata +++ b/src/crypto/err/ssl.errordata @@ -20,7 +20,11 @@ SSL,function,112,SSL_CTX_use_certificate_ASN1 SSL,function,113,SSL_CTX_use_certificate_chain_file SSL,function,114,SSL_CTX_use_certificate_file SSL,function,115,SSL_CTX_use_psk_identity_hint +SSL,function,280,SSL_SESSION_from_bytes SSL,function,116,SSL_SESSION_new +SSL,function,281,SSL_SESSION_parse +SSL,function,150,SSL_SESSION_parse_octet_string +SSL,function,151,SSL_SESSION_parse_string SSL,function,117,SSL_SESSION_print_fp SSL,function,118,SSL_SESSION_set1_id_context SSL,function,119,SSL_SESSION_to_bytes_full @@ -58,8 +62,6 @@ SSL,function,146,SSL_use_certificate_file SSL,function,147,SSL_use_psk_identity_hint SSL,function,148,SSL_write SSL,function,149,d2i_SSL_SESSION -SSL,function,150,d2i_SSL_SESSION_get_octet_string -SSL,function,151,d2i_SSL_SESSION_get_string SSL,function,152,do_ssl3_write SSL,function,153,dtls1_accept SSL,function,154,dtls1_buffer_record @@ -82,6 +84,7 @@ SSL,function,166,i2d_SSL_SESSION SSL,function,167,ssl3_accept SSL,function,169,ssl3_cert_verify_hash SSL,function,170,ssl3_check_cert_and_algorithm +SSL,function,282,ssl3_check_certificate_for_cipher SSL,function,171,ssl3_connect SSL,function,172,ssl3_ctrl SSL,function,173,ssl3_ctx_ctrl diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h index 217dbaf..2735e15 100644 --- a/src/include/openssl/ssl.h +++ b/src/include/openssl/ssl.h @@ -517,6 +517,10 @@ OPENSSL_EXPORT uint32_t SSL_get_mode(const SSL *ssl); OPENSSL_EXPORT int SSL_get_tls_unique(const SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out); +/* SSL_initial_handshake_complete returns one if the initial handshake has + * completed and zero otherwise. */ +OPENSSL_EXPORT int SSL_initial_handshake_complete(const SSL *ssl); + /* Underdocumented functions. * @@ -646,7 +650,12 @@ struct ssl_session_st { * disable session caching and tickets. */ int not_resumable; - /* The cert is the certificate used to establish this connection */ + /* The cert is the certificate used to establish this connection + * + * TODO(davidben): Remove this field. It is not serialized as part of the + * session, but some APIs access it. Certificate-related fields, where not + * redundant with |peer|, should be added to the session. Others should + * probably not be retained across resumptions. */ struct sess_cert_st /* SESS_CERT */ *sess_cert; /* This is the cert for the other end. On clients, it will be the same as @@ -1963,31 +1972,34 @@ OPENSSL_EXPORT int SSL_has_matching_session_id(const SSL *ssl, OPENSSL_EXPORT int SSL_SESSION_to_bytes(SSL_SESSION *in, uint8_t **out_data, size_t *out_len); -/* SSL_SESSION_to_bytes_for_ticket serializes |in|, but excludes the session ID - * which is not necessary in a session ticket. */ +/* SSL_SESSION_to_bytes_for_ticket serializes |in|, but excludes the session + * identification information, namely the session ID and ticket. */ OPENSSL_EXPORT int SSL_SESSION_to_bytes_for_ticket(SSL_SESSION *in, uint8_t **out_data, size_t *out_len); +/* SSL_SESSION_from_bytes parses |in_len| bytes from |in| as an SSL_SESSION. It + * returns a newly-allocated |SSL_SESSION| on success or NULL on error. */ +OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_from_bytes(const uint8_t *in, + size_t in_len); + /* Deprecated: i2d_SSL_SESSION serializes |in| to the bytes pointed to by * |*pp|. On success, it returns the number of bytes written and advances |*pp| * by that many bytes. On failure, it returns -1. If |pp| is NULL, no bytes are * written and only the length is returned. * - * Use SSL_SESSION_to_bytes instead. */ + * Use |SSL_SESSION_to_bytes| instead. */ OPENSSL_EXPORT int i2d_SSL_SESSION(SSL_SESSION *in, uint8_t **pp); -/* d2i_SSL_SESSION deserializes a serialized buffer contained in the |length| - * bytes pointed to by |*pp|. It returns the new SSL_SESSION and advances |*pp| - * by the number of bytes consumed on success and NULL on failure. If |a| is - * NULL, the caller takes ownership of the new session and must call - * |SSL_SESSION_free| when done. +/* Deprecated: d2i_SSL_SESSION parses a serialized session from the |length| + * bytes pointed to by |*pp|. It returns the new |SSL_SESSION| and advances + * |*pp| by the number of bytes consumed on success and NULL on failure. The + * caller takes ownership of the new session and must call |SSL_SESSION_free| + * when done. * - * If |a| and |*a| are not NULL, the SSL_SESSION at |*a| is overridden with the - * deserialized session rather than allocating a new one. In addition, |a| is - * not NULL, but |*a| is, |*a| is set to the new SSL_SESSION. + * If |a| is non-NULL, |*a| is released and set the new |SSL_SESSION|. * - * Passing a value other than NULL to |a| is deprecated. */ + * Use |SSL_SESSION_from_bytes| instead. */ OPENSSL_EXPORT SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length); @@ -2621,8 +2633,8 @@ OPENSSL_EXPORT const char *SSLeay_version(int unused); #define SSL_F_SSL_use_psk_identity_hint 147 #define SSL_F_SSL_write 148 #define SSL_F_d2i_SSL_SESSION 149 -#define SSL_F_d2i_SSL_SESSION_get_octet_string 150 -#define SSL_F_d2i_SSL_SESSION_get_string 151 +#define SSL_F_SSL_SESSION_parse_octet_string 150 +#define SSL_F_SSL_SESSION_parse_string 151 #define SSL_F_do_ssl3_write 152 #define SSL_F_dtls1_accept 153 #define SSL_F_dtls1_buffer_record 154 @@ -2747,6 +2759,9 @@ OPENSSL_EXPORT const char *SSLeay_version(int unused); #define SSL_F_SSL_AEAD_CTX_open 277 #define SSL_F_SSL_AEAD_CTX_seal 278 #define SSL_F_dtls1_seal_record 279 +#define SSL_F_SSL_SESSION_from_bytes 280 +#define SSL_F_SSL_SESSION_parse 281 +#define SSL_F_ssl3_check_certificate_for_cipher 282 #define SSL_R_APP_DATA_IN_HANDSHAKE 100 #define SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT 101 #define SSL_R_BAD_ALERT 102 diff --git a/src/ssl/internal.h b/src/ssl/internal.h index 7d9a5ad..4d70431 100644 --- a/src/ssl/internal.h +++ b/src/ssl/internal.h @@ -581,15 +581,13 @@ typedef struct cert_st { } CERT; typedef struct sess_cert_st { - STACK_OF(X509) *cert_chain; /* as received from peer (not for SSL2) */ + /* cert_chain is the certificate chain sent by the peer. NOTE: for a client, + * this does includes the server's leaf certificate, but, for a server, this + * does NOT include the client's leaf. */ + STACK_OF(X509) *cert_chain; - /* The 'peer_...' members are used only by clients. */ - int peer_cert_type; - - CERT_PKEY *peer_key; /* points to an element of peer_pkeys (never NULL!) */ - CERT_PKEY peer_pkeys[SSL_PKEY_NUM]; - /* Obviously we don't have the private keys of these, - * so maybe we shouldn't even use the CERT_PKEY type here. */ + /* peer_cert, on a client, is the leaf certificate of the peer. */ + X509 *peer_cert; DH *peer_dh_tmp; EC_KEY *peer_ecdh_tmp; @@ -799,8 +797,8 @@ CERT *ssl_cert_dup(CERT *cert); void ssl_cert_clear_certs(CERT *c); void ssl_cert_free(CERT *c); SESS_CERT *ssl_sess_cert_new(void); -void ssl_sess_cert_free(SESS_CERT *sc); -int ssl_set_peer_cert_type(SESS_CERT *c, int type); +SESS_CERT *ssl_sess_cert_dup(const SESS_CERT *sess_cert); +void ssl_sess_cert_free(SESS_CERT *sess_cert); int ssl_get_new_session(SSL *s, int session); int ssl_get_prev_session(SSL *s, const struct ssl_early_callback_ctx *ctx); STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs); diff --git a/src/ssl/s3_clnt.c b/src/ssl/s3_clnt.c index 159e2d7..d334e1d 100644 --- a/src/ssl/s3_clnt.c +++ b/src/ssl/s3_clnt.c @@ -1028,10 +1028,8 @@ int ssl3_get_server_certificate(SSL *s) { SSL_R_WRONG_CERTIFICATE_TYPE); goto f_err; } - sc->peer_cert_type = i; - X509_free(sc->peer_pkeys[i].x509); - sc->peer_pkeys[i].x509 = X509_up_ref(x); - sc->peer_key = &(sc->peer_pkeys[i]); + X509_free(sc->peer_cert); + sc->peer_cert = X509_up_ref(x); X509_free(s->session->peer); s->session->peer = X509_up_ref(x); @@ -1195,8 +1193,7 @@ int ssl3_get_server_key_exchange(SSL *s) { } if (alg_a & SSL_aRSA) { - pkey = X509_get_pubkey( - s->session->sess_cert->peer_pkeys[SSL_PKEY_RSA_ENC].x509); + pkey = X509_get_pubkey(s->session->sess_cert->peer_cert); } /* else anonymous DH, so no certificate or pkey. */ @@ -1258,14 +1255,12 @@ int ssl3_get_server_key_exchange(SSL *s) { /* The ECC/TLS specification does not mention the use of DSA to sign * ECParameters in the server key exchange message. We do support RSA and * ECDSA. */ - if (alg_a & SSL_aRSA) { - pkey = X509_get_pubkey( - s->session->sess_cert->peer_pkeys[SSL_PKEY_RSA_ENC].x509); - } else if (alg_a & SSL_aECDSA) { - pkey = - X509_get_pubkey(s->session->sess_cert->peer_pkeys[SSL_PKEY_ECC].x509); + if (alg_a & (SSL_aRSA|SSL_aECDSA)) { + pkey = X509_get_pubkey(s->session->sess_cert->peer_cert); + } else { + /* Otherwise this is ECDHE_PSK, so no public key. */ + assert(alg_a == SSL_aPSK); } - /* else anonymous ECDH, so no certificate or pkey. */ EC_KEY_set_public_key(ecdh, srvr_ecpoint); s->session->sess_cert->peer_ecdh_tmp = ecdh; ecdh = NULL; @@ -1508,6 +1503,36 @@ int ssl3_get_new_session_ticket(SSL *s) { return n; } + if (s->hit) { + /* The server is sending a new ticket for an existing session. Sessions are + * immutable once established, so duplicate all but the ticket of the + * existing session. */ + uint8_t *bytes; + size_t bytes_len; + if (!SSL_SESSION_to_bytes_for_ticket(s->session, &bytes, &bytes_len)) { + goto err; + } + SSL_SESSION *new_session = SSL_SESSION_from_bytes(bytes, bytes_len); + OPENSSL_free(bytes); + if (new_session == NULL) { + /* This should never happen. */ + OPENSSL_PUT_ERROR(SSL, ssl3_get_new_session_ticket, ERR_R_INTERNAL_ERROR); + goto err; + } + if (s->session->sess_cert != NULL) { + /* |sess_cert| is not serialized and must be duplicated explicitly. */ + assert(new_session->sess_cert == NULL); + new_session->sess_cert = ssl_sess_cert_dup(s->session->sess_cert); + if (new_session->sess_cert == NULL) { + SSL_SESSION_free(new_session); + goto err; + } + } + + SSL_SESSION_free(s->session); + s->session = new_session; + } + CBS_init(&new_session_ticket, s->init_msg, n); if (!CBS_get_u32(&new_session_ticket, @@ -1702,8 +1727,7 @@ int ssl3_send_client_key_exchange(SSL *s) { goto err; } - pkey = X509_get_pubkey( - s->session->sess_cert->peer_pkeys[SSL_PKEY_RSA_ENC].x509); + pkey = X509_get_pubkey(s->session->sess_cert->peer_cert); if (pkey == NULL || pkey->type != EVP_PKEY_RSA || pkey->pkey.rsa == NULL) { @@ -2159,9 +2183,7 @@ int ssl3_send_client_certificate(SSL *s) { #define has_bits(i, m) (((i) & (m)) == (m)) int ssl3_check_cert_and_algorithm(SSL *s) { - int i, idx; long alg_k, alg_a; - EVP_PKEY *pkey = NULL; SESS_CERT *sc; DH *dh; @@ -2181,11 +2203,9 @@ int ssl3_check_cert_and_algorithm(SSL *s) { dh = s->session->sess_cert->peer_dh_tmp; - /* This is the passed certificate */ - - idx = sc->peer_cert_type; - if (idx == SSL_PKEY_ECC) { - if (ssl_check_srvr_ecc_cert_and_alg(sc->peer_pkeys[idx].x509, s) == 0) { + int cert_type = X509_certificate_type(sc->peer_cert, NULL); + if (cert_type & EVP_PK_EC) { + if (ssl_check_srvr_ecc_cert_and_alg(sc->peer_cert, s) == 0) { /* check failed */ OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, SSL_R_BAD_ECC_CERT); goto f_err; @@ -2197,25 +2217,21 @@ int ssl3_check_cert_and_algorithm(SSL *s) { SSL_R_MISSING_ECDSA_SIGNING_CERT); goto f_err; } - pkey = X509_get_pubkey(sc->peer_pkeys[idx].x509); - i = X509_certificate_type(sc->peer_pkeys[idx].x509, pkey); - EVP_PKEY_free(pkey); /* Check that we have a certificate if we require one */ - if ((alg_a & SSL_aRSA) && !has_bits(i, EVP_PK_RSA | EVP_PKT_SIGN)) { + if ((alg_a & SSL_aRSA) && !has_bits(cert_type, EVP_PK_RSA | EVP_PKT_SIGN)) { OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, SSL_R_MISSING_RSA_SIGNING_CERT); goto f_err; } - if ((alg_k & SSL_kRSA) && !has_bits(i, EVP_PK_RSA | EVP_PKT_ENC)) { + if ((alg_k & SSL_kRSA) && !has_bits(cert_type, EVP_PK_RSA | EVP_PKT_ENC)) { OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, SSL_R_MISSING_RSA_ENCRYPTING_CERT); goto f_err; } - if ((alg_k & SSL_kDHE) && - !(has_bits(i, EVP_PK_DH | EVP_PKT_EXCH) || dh != NULL)) { + if ((alg_k & SSL_kDHE) && dh == NULL) { OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, SSL_R_MISSING_DH_KEY); goto f_err; } diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c index d1ac1b6..76052df 100644 --- a/src/ssl/ssl_asn1.c +++ b/src/ssl/ssl_asn1.c @@ -261,7 +261,7 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, } } - if (in->tlsext_tick) { + if (in->tlsext_tick && !for_ticket) { if (!CBB_add_asn1(&session, &child, kTicketTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->tlsext_tick, in->tlsext_ticklen)) { @@ -360,26 +360,27 @@ int i2d_SSL_SESSION(SSL_SESSION *in, uint8_t **pp) { return len; } -/* d2i_SSL_SESSION_get_string gets an optional ASN.1 OCTET STRING +/* SSL_SESSION_parse_string gets an optional ASN.1 OCTET STRING * explicitly tagged with |tag| from |cbs| and saves it in |*out|. On * entry, if |*out| is not NULL, it frees the existing contents. If * the element was not found, it sets |*out| to NULL. It returns one * on success, whether or not the element was found, and zero on * decode error. */ -static int d2i_SSL_SESSION_get_string(CBS *cbs, char **out, unsigned tag) { +static int SSL_SESSION_parse_string(CBS *cbs, char **out, unsigned tag) { CBS value; int present; if (!CBS_get_optional_asn1_octet_string(cbs, &value, &present, tag)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse_string, SSL_R_INVALID_SSL_SESSION); return 0; } if (present) { if (CBS_contains_zero_byte(&value)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse_string, + SSL_R_INVALID_SSL_SESSION); return 0; } if (!CBS_strdup(&value, out)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse_string, ERR_R_MALLOC_FAILURE); return 0; } } else { @@ -389,45 +390,42 @@ static int d2i_SSL_SESSION_get_string(CBS *cbs, char **out, unsigned tag) { return 1; } -/* d2i_SSL_SESSION_get_string gets an optional ASN.1 OCTET STRING +/* SSL_SESSION_parse_string gets an optional ASN.1 OCTET STRING * explicitly tagged with |tag| from |cbs| and stows it in |*out_ptr| * and |*out_len|. If |*out_ptr| is not NULL, it frees the existing * contents. On entry, if the element was not found, it sets * |*out_ptr| to NULL. It returns one on success, whether or not the * element was found, and zero on decode error. */ -static int d2i_SSL_SESSION_get_octet_string(CBS *cbs, uint8_t **out_ptr, +static int SSL_SESSION_parse_octet_string(CBS *cbs, uint8_t **out_ptr, size_t *out_len, unsigned tag) { CBS value; if (!CBS_get_optional_asn1_octet_string(cbs, &value, NULL, tag)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse_octet_string, + SSL_R_INVALID_SSL_SESSION); return 0; } if (!CBS_stow(&value, out_ptr, out_len)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse_octet_string, + ERR_R_MALLOC_FAILURE); return 0; } return 1; } -SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { - SSL_SESSION *ret, *allocated = NULL; - CBS cbs, session, cipher, session_id, master_key; +static SSL_SESSION *SSL_SESSION_parse(CBS *cbs) { + SSL_SESSION *ret = NULL; + CBS session, cipher, session_id, master_key; CBS peer, sid_ctx, peer_sha256, original_handshake_hash; int has_peer, has_peer_sha256, extended_master_secret; uint64_t version, ssl_version; uint64_t session_time, timeout, verify_result, ticket_lifetime_hint; - if (a && *a) { - ret = *a; - } else { - ret = allocated = SSL_SESSION_new(); - if (allocated == NULL) { - goto err; - } + ret = SSL_SESSION_new(); + if (ret == NULL) { + goto err; } - CBS_init(&cbs, *pp, length); - if (!CBS_get_asn1(&cbs, &session, CBS_ASN1_SEQUENCE) || + if (!CBS_get_asn1(cbs, &session, CBS_ASN1_SEQUENCE) || !CBS_get_asn1_uint64(&session, &version) || !CBS_get_asn1_uint64(&session, &ssl_version) || !CBS_get_asn1(&session, &cipher, CBS_ASN1_OCTETSTRING) || @@ -441,21 +439,21 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { kSessionIDContextTag) || !CBS_get_optional_asn1_uint64(&session, &verify_result, kVerifyResultTag, X509_V_OK)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } - if (!d2i_SSL_SESSION_get_string(&session, &ret->tlsext_hostname, - kHostNameTag) || - !d2i_SSL_SESSION_get_string(&session, &ret->psk_identity, - kPSKIdentityTag)) { + if (!SSL_SESSION_parse_string(&session, &ret->tlsext_hostname, + kHostNameTag) || + !SSL_SESSION_parse_string(&session, &ret->psk_identity, + kPSKIdentityTag)) { goto err; } if (!CBS_get_optional_asn1_uint64(&session, &ticket_lifetime_hint, kTicketLifetimeHintTag, 0)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } - if (!d2i_SSL_SESSION_get_octet_string(&session, &ret->tlsext_tick, + if (!SSL_SESSION_parse_octet_string(&session, &ret->tlsext_tick, &ret->tlsext_ticklen, kTicketTag)) { goto err; } @@ -463,14 +461,14 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { &has_peer_sha256, kPeerSHA256Tag) || !CBS_get_optional_asn1_octet_string(&session, &original_handshake_hash, NULL, kOriginalHandshakeHashTag)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } - if (!d2i_SSL_SESSION_get_octet_string( + if (!SSL_SESSION_parse_octet_string( &session, &ret->tlsext_signed_cert_timestamp_list, &ret->tlsext_signed_cert_timestamp_list_length, kSignedCertTimestampListTag) || - !d2i_SSL_SESSION_get_octet_string( + !SSL_SESSION_parse_octet_string( &session, &ret->ocsp_response, &ret->ocsp_response_length, kOCSPResponseTag)) { goto err; @@ -479,44 +477,44 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { kExtendedMasterSecretTag, 0 /* default to false */) || CBS_len(&session) != 0) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } ret->extended_master_secret = extended_master_secret; if (version != SSL_SESSION_ASN1_VERSION) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } /* Only support SSLv3/TLS and DTLS. */ if ((ssl_version >> 8) != SSL3_VERSION_MAJOR && (ssl_version >> 8) != (DTLS1_VERSION >> 8)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_UNKNOWN_SSL_VERSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_UNKNOWN_SSL_VERSION); goto err; } ret->ssl_version = ssl_version; uint16_t cipher_value; if (!CBS_get_u16(&cipher, &cipher_value) || CBS_len(&cipher) != 0) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_CIPHER_CODE_WRONG_LENGTH); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_CIPHER_CODE_WRONG_LENGTH); goto err; } ret->cipher = SSL_get_cipher_by_value(cipher_value); if (ret->cipher == NULL) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_UNSUPPORTED_CIPHER); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_UNSUPPORTED_CIPHER); goto err; } if (CBS_len(&session_id) > SSL3_MAX_SSL_SESSION_ID_LENGTH) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } memcpy(ret->session_id, CBS_data(&session_id), CBS_len(&session_id)); ret->session_id_length = CBS_len(&session_id); if (CBS_len(&master_key) > SSL_MAX_MASTER_KEY_LENGTH) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } memcpy(ret->master_key, CBS_data(&master_key), CBS_len(&master_key)); @@ -524,7 +522,7 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { if (session_time > LONG_MAX || timeout > LONG_MAX) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } ret->time = session_time; @@ -540,13 +538,13 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { goto err; } if (ptr != CBS_data(&peer) + CBS_len(&peer)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } } if (CBS_len(&sid_ctx) > sizeof(ret->sid_ctx)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } memcpy(ret->sid_ctx, CBS_data(&sid_ctx), CBS_len(&sid_ctx)); @@ -554,7 +552,7 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { if (verify_result > LONG_MAX || ticket_lifetime_hint > 0xffffffff) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } ret->verify_result = verify_result; @@ -562,7 +560,7 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { if (has_peer_sha256) { if (CBS_len(&peer_sha256) != sizeof(ret->peer_sha256)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } memcpy(ret->peer_sha256, CBS_data(&peer_sha256), sizeof(ret->peer_sha256)); @@ -573,20 +571,53 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { if (CBS_len(&original_handshake_hash) > sizeof(ret->original_handshake_hash)) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); goto err; } memcpy(ret->original_handshake_hash, CBS_data(&original_handshake_hash), CBS_len(&original_handshake_hash)); ret->original_handshake_hash_len = CBS_len(&original_handshake_hash); + return ret; + +err: + SSL_SESSION_free(ret); + return NULL; +} + +SSL_SESSION *SSL_SESSION_from_bytes(const uint8_t *in, size_t in_len) { + CBS cbs; + CBS_init(&cbs, in, in_len); + SSL_SESSION *ret = SSL_SESSION_parse(&cbs); + if (ret == NULL) { + return NULL; + } + if (CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_from_bytes, SSL_R_INVALID_SSL_SESSION); + SSL_SESSION_free(ret); + return NULL; + } + return ret; +} + +SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { + if (length < 0) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, ERR_R_INTERNAL_ERROR); + return NULL; + } + + CBS cbs; + CBS_init(&cbs, *pp, length); + + SSL_SESSION *ret = SSL_SESSION_parse(&cbs); + if (ret == NULL) { + return NULL; + } + if (a) { + SSL_SESSION_free(*a); *a = ret; } *pp = CBS_data(&cbs); return ret; - -err: - SSL_SESSION_free(allocated); - return NULL; } diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c index f1fd675..85aa079 100644 --- a/src/ssl/ssl_cert.c +++ b/src/ssl/ssl_cert.c @@ -119,11 +119,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include "../crypto/dh/internal.h" @@ -409,33 +411,48 @@ SESS_CERT *ssl_sess_cert_new(void) { } memset(ret, 0, sizeof *ret); - ret->peer_key = &(ret->peer_pkeys[SSL_PKEY_RSA_ENC]); return ret; } -void ssl_sess_cert_free(SESS_CERT *sc) { - int i; - - if (sc == NULL) { - return; +SESS_CERT *ssl_sess_cert_dup(const SESS_CERT *sess_cert) { + SESS_CERT *ret = ssl_sess_cert_new(); + if (ret == NULL) { + return NULL; } - sk_X509_pop_free(sc->cert_chain, X509_free); - - for (i = 0; i < SSL_PKEY_NUM; i++) { - X509_free(sc->peer_pkeys[i].x509); + if (sess_cert->cert_chain != NULL) { + ret->cert_chain = X509_chain_up_ref(sess_cert->cert_chain); + if (ret->cert_chain == NULL) { + ssl_sess_cert_free(ret); + return NULL; + } } + if (sess_cert->peer_cert != NULL) { + ret->peer_cert = X509_up_ref(sess_cert->peer_cert); + } + if (sess_cert->peer_dh_tmp != NULL) { + ret->peer_dh_tmp = sess_cert->peer_dh_tmp; + DH_up_ref(ret->peer_dh_tmp); + } + if (sess_cert->peer_ecdh_tmp != NULL) { + ret->peer_ecdh_tmp = sess_cert->peer_ecdh_tmp; + EC_KEY_up_ref(ret->peer_ecdh_tmp); + } + return ret; +} - DH_free(sc->peer_dh_tmp); - EC_KEY_free(sc->peer_ecdh_tmp); +void ssl_sess_cert_free(SESS_CERT *sess_cert) { + if (sess_cert == NULL) { + return; + } - OPENSSL_free(sc); -} + sk_X509_pop_free(sess_cert->cert_chain, X509_free); + X509_free(sess_cert->peer_cert); + DH_free(sess_cert->peer_dh_tmp); + EC_KEY_free(sess_cert->peer_ecdh_tmp); -int ssl_set_peer_cert_type(SESS_CERT *sc, int type) { - sc->peer_cert_type = type; - return 1; + OPENSSL_free(sess_cert); } int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk) { diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c index e95226f..9e1e308 100644 --- a/src/ssl/ssl_lib.c +++ b/src/ssl/ssl_lib.c @@ -1975,8 +1975,16 @@ void ssl_update_cache(SSL *s, int mode) { return; } + int has_new_session = !s->hit; + if (!s->server && s->tlsext_ticket_expected) { + /* A client may see new sessions on abbreviated handshakes if the server + * decides to renew the ticket. Once the handshake is completed, it should + * be inserted into the cache. */ + has_new_session = 1; + } + SSL_CTX *ctx = s->initial_ctx; - if ((ctx->session_cache_mode & mode) == mode && !s->hit && + if ((ctx->session_cache_mode & mode) == mode && has_new_session && ((ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_STORE) || SSL_CTX_add_session(ctx, s->session)) && ctx->new_session_cb != NULL) { @@ -2960,6 +2968,10 @@ err: return 0; } +int SSL_initial_handshake_complete(const SSL *ssl) { + return ssl->s3->initial_handshake_complete; +} + int SSL_CTX_sess_connect(const SSL_CTX *ctx) { return 0; } int SSL_CTX_sess_connect_good(const SSL_CTX *ctx) { return 0; } int SSL_CTX_sess_connect_renegotiate(const SSL_CTX *ctx) { return 0; } diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc index 1c6e24a..9f2ddb9 100644 --- a/src/ssl/ssl_test.cc +++ b/src/ssl/ssl_test.cc @@ -359,6 +359,18 @@ static const char kBadSessionVersion[] = "q+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdAi4gv7Y5oliynrSIEIAYGBgYG" "BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrgMEAQevAwQBBLADBAEF"; +// kBadSessionTrailingData is a custom serialized SSL_SESSION with trailing data +// appended. +static const char kBadSessionTrailingData[] = + "MIIBdgIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ" + "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH" + "IWoJoQYCBFRDO46iBAICASykAwQBAqUDAgEUphAEDnd3dy5nb29nbGUuY29tqAcE" + "BXdvcmxkqQUCAwGJwKqBpwSBpBwUQvoeOk0Kg36SYTcLEkXqKwOBfF9vE4KX0Nxe" + "LwjcDTpsuh3qXEaZ992r1N38VDcyS6P7I6HBYN9BsNHM362zZnY27GpTw+Kwd751" + "CLoXFPoaMOe57dbBpXoro6Pd3BTbf/Tzr88K06yEOTDKPNj3+inbMaVigtK4PLyP" + "q+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdAi4gv7Y5oliynrSIEIAYGBgYG" + "BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrgMEAQevAwQBBLADBAEFAAAA"; + static bool DecodeBase64(std::vector *out, const char *in) { size_t len; if (!EVP_DecodedLength(&len, strlen(in))) { @@ -387,10 +399,10 @@ static bool TestSSL_SESSIONEncoding(const char *input_b64) { } // Verify the SSL_SESSION decodes. - cptr = bssl::vector_data(&input); - ScopedSSL_SESSION session(d2i_SSL_SESSION(NULL, &cptr, input.size())); - if (!session || cptr != bssl::vector_data(&input) + input.size()) { - fprintf(stderr, "d2i_SSL_SESSION failed\n"); + ScopedSSL_SESSION session(SSL_SESSION_from_bytes(bssl::vector_data(&input), + input.size())); + if (!session) { + fprintf(stderr, "SSL_SESSION_from_bytes failed\n"); return false; } @@ -409,6 +421,14 @@ static bool TestSSL_SESSIONEncoding(const char *input_b64) { return false; } + // Verify the SSL_SESSION also decodes with the legacy API. + cptr = bssl::vector_data(&input); + session.reset(d2i_SSL_SESSION(NULL, &cptr, input.size())); + if (!session || cptr != bssl::vector_data(&input) + input.size()) { + fprintf(stderr, "d2i_SSL_SESSION failed\n"); + return false; + } + // Verify the SSL_SESSION encoding round-trips via the legacy API. int len = i2d_SSL_SESSION(session.get(), NULL); if (len < 0 || (size_t)len != input.size()) { @@ -447,10 +467,10 @@ static bool TestBadSSL_SESSIONEncoding(const char *input_b64) { } // Verify that the SSL_SESSION fails to decode. - const uint8_t *ptr = bssl::vector_data(&input); - ScopedSSL_SESSION session(d2i_SSL_SESSION(NULL, &ptr, input.size())); + ScopedSSL_SESSION session(SSL_SESSION_from_bytes(bssl::vector_data(&input), + input.size())); if (session) { - fprintf(stderr, "d2i_SSL_SESSION unexpectedly succeeded\n"); + fprintf(stderr, "SSL_SESSION_from_bytes unexpectedly succeeded\n"); return false; } ERR_clear_error(); @@ -537,6 +557,7 @@ int main(void) { !TestSSL_SESSIONEncoding(kCustomSession) || !TestBadSSL_SESSIONEncoding(kBadSessionExtraField) || !TestBadSSL_SESSIONEncoding(kBadSessionVersion) || + !TestBadSSL_SESSIONEncoding(kBadSessionTrailingData) || !TestDefaultVersion(0, &TLS_method) || !TestDefaultVersion(SSL3_VERSION, &SSLv3_method) || !TestDefaultVersion(TLS1_VERSION, &TLSv1_method) || diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c index 9e5c011..213a647 100644 --- a/src/ssl/t1_lib.c +++ b/src/ssl/t1_lib.c @@ -2184,7 +2184,7 @@ static int tls_decrypt_ticket(SSL *s, const uint8_t *etick, int eticklen, EVP_CIPHER_CTX_cleanup(&ctx); p = sdec; - sess = d2i_SSL_SESSION(NULL, &p, slen); + sess = SSL_SESSION_from_bytes(sdec, slen); OPENSSL_free(sdec); if (sess) { /* The session ID, if non-empty, is used by some clients to detect that the diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc index 40cb149..3b95d7e 100644 --- a/src/ssl/test/bssl_shim.cc +++ b/src/ssl/test/bssl_shim.cc @@ -838,6 +838,20 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, 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.get()) == nullptr) { + fprintf(stderr, "Missing peer certificate chain!\n"); + return false; + } + } else if (SSL_get_peer_cert_chain(ssl.get()) != nullptr) { + fprintf(stderr, "Unexpected peer certificate chain!\n"); + return false; + } + } } if (config->export_keying_material > 0) { -- cgit v1.1 From 98856d4b9dc1a59a576816dbb097aa9d9e6de47a Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Tue, 23 Jun 2015 16:23:41 -0700 Subject: Fix for CVE-2015-1789. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X509_cmp_time does not properly check the length of the ASN1_TIME string and can read a few bytes out of bounds. In addition, X509_cmp_time accepts an arbitrary number of fractional seconds in the time string. An attacker can use this to craft malformed certificates and CRLs of various sizes and potentially cause a segmentation fault, resulting in a DoS on applications that verify certificates or CRLs. TLS clients that verify CRLs are affected. TLS clients and servers with client authentication enabled may be affected if they use custom verification callbacks. This change cherry-picks the following changes from BoringSSL: d87021d2 – Fix length checks in X509_cmp_time to avoid out-of-bounds reads. Change-Id: Ia7d0c5d889f61a3c4be6ea79a5ab41f67bc3c65c --- src/crypto/x509/x509_vfy.c | 54 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/crypto/x509/x509_vfy.c b/src/crypto/x509/x509_vfy.c index 2ba9c84..f53f279 100644 --- a/src/crypto/x509/x509_vfy.c +++ b/src/crypto/x509/x509_vfy.c @@ -1829,49 +1829,89 @@ int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time) ASN1_TIME atm; long offset; char buff1[24],buff2[24],*p; - int i,j; + int i, j, remaining; p=buff1; - i=ctm->length; + remaining = ctm->length; str=(char *)ctm->data; + /* Note that the following (historical) code allows much more slack in + * the time format than RFC5280. In RFC5280, the representation is + * fixed: + * UTCTime: YYMMDDHHMMSSZ + * GeneralizedTime: YYYYMMDDHHMMSSZ */ if (ctm->type == V_ASN1_UTCTIME) { - if ((i < 11) || (i > 17)) return 0; + /* YYMMDDHHMM[SS]Z or YYMMDDHHMM[SS](+-)hhmm */ + int min_length = sizeof("YYMMDDHHMMZ") - 1; + int max_length = sizeof("YYMMDDHHMMSS+hhmm") - 1; + if (remaining < min_length || remaining > max_length) + return 0; memcpy(p,str,10); p+=10; str+=10; + remaining -= 10; } else { - if (i < 13) return 0; + /* YYYYMMDDHHMM[SS[.fff]]Z or YYYYMMDDHHMM[SS[.f[f[f]]]](+-)hhmm */ + int min_length = sizeof("YYYYMMDDHHMMZ") - 1; + int max_length = sizeof("YYYYMMDDHHMMSS.fff+hhmm") - 1; + if (remaining < min_length || remaining > max_length) + return 0; memcpy(p,str,12); p+=12; str+=12; + remaining -= 12; } if ((*str == 'Z') || (*str == '-') || (*str == '+')) { *(p++)='0'; *(p++)='0'; } else { + /* SS (seconds) */ + if (remaining < 2) + return 0; *(p++)= *(str++); *(p++)= *(str++); - /* Skip any fractional seconds... */ - if (*str == '.') + remaining -= 2; + /* Skip any (up to three) fractional seconds... + * TODO(emilia): in RFC5280, fractional seconds are forbidden. + * Can we just kill them altogether? */ + if (remaining && *str == '.') { str++; - while ((*str >= '0') && (*str <= '9')) str++; + remaining--; + for (i = 0; i < 3 && remaining; i++, str++, remaining--) + { + if (*str < '0' || *str > '9') + break; + } } } *(p++)='Z'; *(p++)='\0'; + /* We now need either a terminating 'Z' or an offset. */ + if (!remaining) + return 0; if (*str == 'Z') + { + if (remaining != 1) + return 0; offset=0; + } else { + /* (+-)HHMM */ if ((*str != '+') && (*str != '-')) return 0; + /* Historical behaviour: the (+-)hhmm offset is forbidden in RFC5280. */ + if (remaining != 5) + return 0; + if (str[1] < '0' || str[1] > '9' || str[2] < '0' || str[2] > '9' || + str[3] < '0' || str[3] > '9' || str[4] < '0' || str[4] > '9') + return 0; offset=((str[1]-'0')*10+(str[2]-'0'))*60; offset+=(str[3]-'0')*10+(str[4]-'0'); if (*str == '-') -- cgit v1.1 From e0846beeb321f7d3170e4e389950b12fce69ab10 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Tue, 23 Jun 2015 16:25:33 -0700 Subject: dsa_pub_encode: Write out DSA parameters (p, q, g) in addition to key. This change cherry-picks BoringSSL's e65886a5. Change-Id: I63d5dc280d420b64b658bfd85f180a01adb8a18b --- src/crypto/evp/p_dsa_asn1.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/crypto/evp/p_dsa_asn1.c b/src/crypto/evp/p_dsa_asn1.c index 0ac7da7..826d4e4 100644 --- a/src/crypto/evp/p_dsa_asn1.c +++ b/src/crypto/evp/p_dsa_asn1.c @@ -129,21 +129,37 @@ err: static int dsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) { DSA *dsa; - void *pval = NULL; + ASN1_STRING *pval = NULL; uint8_t *penc = NULL; int penclen; dsa = pkey->pkey.dsa; dsa->write_params = 0; - penclen = i2d_DSAPublicKey(dsa, &penc); + int ptype; + if (dsa->p && dsa->q && dsa->g) { + pval = ASN1_STRING_new(); + if (!pval) { + OPENSSL_PUT_ERROR(EVP, dsa_pub_encode, ERR_R_MALLOC_FAILURE); + goto err; + } + pval->length = i2d_DSAparams(dsa, &pval->data); + if (pval->length <= 0) { + OPENSSL_PUT_ERROR(EVP, dsa_pub_encode, ERR_R_MALLOC_FAILURE); + goto err; + } + ptype = V_ASN1_SEQUENCE; + } else { + ptype = V_ASN1_UNDEF; + } + penclen = i2d_DSAPublicKey(dsa, &penc); if (penclen <= 0) { OPENSSL_PUT_ERROR(EVP, dsa_pub_encode, ERR_R_MALLOC_FAILURE); goto err; } - if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_DSA), V_ASN1_UNDEF, pval, + if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_DSA), ptype, pval, penc, penclen)) { return 1; } -- cgit v1.1 From bd9957e6e28506c4431ce8d3cadbc0a04905b15e Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Tue, 23 Jun 2015 16:28:07 -0700 Subject: Handle RDRAND failures. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I mistakenly believed that only RDSEED could fail. However, the Intel manuals state that RDRAND can fail too. This change cherry-picks the following BoringSSL changes: 2cac3506 – Handle RDRAND failures. 248abbd7 – Add missing comma in .type pragma for rdrand code. Change-Id: Icdc56a50ce36e9c525063583882c676a5312d313 --- linux-x86_64/crypto/rand/rdrand-x86_64.S | 39 +++++++++++++++++++++++- mac-x86_64/crypto/rand/rdrand-x86_64.S | 39 +++++++++++++++++++++++- src/crypto/rand/asm/rdrand-x86_64.pl | 52 +++++++++++++++++++++++++++++++- src/crypto/rand/hwrand.c | 30 +++++++++++------- src/crypto/rand/internal.h | 5 +-- src/crypto/rand/rand.c | 5 ++- win-x86_64/crypto/rand/rdrand-x86_64.asm | 50 +++++++++++++++++++++++++++++- 7 files changed, 200 insertions(+), 20 deletions(-) diff --git a/linux-x86_64/crypto/rand/rdrand-x86_64.S b/linux-x86_64/crypto/rand/rdrand-x86_64.S index 622ae55..94aab9c 100644 --- a/linux-x86_64/crypto/rand/rdrand-x86_64.S +++ b/linux-x86_64/crypto/rand/rdrand-x86_64.S @@ -1,11 +1,48 @@ #if defined(__x86_64__) .text + + + .globl CRYPTO_rdrand .hidden CRYPTO_rdrand .type CRYPTO_rdrand,@function .align 16 CRYPTO_rdrand: -.byte 0x48, 0x0f, 0xc7, 0xf0 + xorq %rax,%rax + + +.byte 0x48, 0x0f, 0xc7, 0xf1 + + adcq %rax,%rax + movq %rcx,0(%rdi) + .byte 0xf3,0xc3 + + + + + +.globl CRYPTO_rdrand_multiple8_buf +.hidden CRYPTO_rdrand_multiple8_buf +.type CRYPTO_rdrand_multiple8_buf,@function +.align 16 +CRYPTO_rdrand_multiple8_buf: + testq %rsi,%rsi + jz .Lout + movq $8,%rdx +.Lloop: + + +.byte 0x48, 0x0f, 0xc7, 0xf1 + jnc .Lerr + movq %rcx,0(%rdi) + addq %rdx,%rdi + subq %rdx,%rsi + jnz .Lloop +.Lout: + movq $1,%rax + .byte 0xf3,0xc3 +.Lerr: + xorq %rax,%rax .byte 0xf3,0xc3 #endif diff --git a/mac-x86_64/crypto/rand/rdrand-x86_64.S b/mac-x86_64/crypto/rand/rdrand-x86_64.S index 1ba990f..f0df296 100644 --- a/mac-x86_64/crypto/rand/rdrand-x86_64.S +++ b/mac-x86_64/crypto/rand/rdrand-x86_64.S @@ -1,11 +1,48 @@ #if defined(__x86_64__) .text + + + .globl _CRYPTO_rdrand .private_extern _CRYPTO_rdrand .p2align 4 _CRYPTO_rdrand: -.byte 0x48, 0x0f, 0xc7, 0xf0 + xorq %rax,%rax + + +.byte 0x48, 0x0f, 0xc7, 0xf1 + + adcq %rax,%rax + movq %rcx,0(%rdi) + .byte 0xf3,0xc3 + + + + + +.globl _CRYPTO_rdrand_multiple8_buf +.private_extern _CRYPTO_rdrand_multiple8_buf + +.p2align 4 +_CRYPTO_rdrand_multiple8_buf: + testq %rsi,%rsi + jz L$out + movq $8,%rdx +L$loop: + + +.byte 0x48, 0x0f, 0xc7, 0xf1 + jnc L$err + movq %rcx,0(%rdi) + addq %rdx,%rdi + subq %rdx,%rsi + jnz L$loop +L$out: + movq $1,%rax + .byte 0xf3,0xc3 +L$err: + xorq %rax,%rax .byte 0xf3,0xc3 #endif diff --git a/src/crypto/rand/asm/rdrand-x86_64.pl b/src/crypto/rand/asm/rdrand-x86_64.pl index a917611..c32a55c 100644 --- a/src/crypto/rand/asm/rdrand-x86_64.pl +++ b/src/crypto/rand/asm/rdrand-x86_64.pl @@ -1,5 +1,19 @@ #!/usr/bin/env perl +# Copyright (c) 2015, Google Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + $flavour = shift; $output = shift; if ($flavour =~ /\./) { $output = $flavour; undef $flavour; } @@ -14,11 +28,47 @@ open OUT,"| \"$^X\" $xlate $flavour $output"; print<<___; .text +# CRYPTO_rdrand writes eight bytes of random data from the hardware RNG to +# |out|. It returns one on success or zero on hardware failure. +# int CRYPTO_rdrand(uint8_t out[8]); .globl CRYPTO_rdrand .type CRYPTO_rdrand,\@function,1 .align 16 CRYPTO_rdrand: - .byte 0x48, 0x0f, 0xc7, 0xf0 + xorq %rax, %rax + # This is rdrand %rcx. It sets rcx to a random value and sets the carry + # flag on success. + .byte 0x48, 0x0f, 0xc7, 0xf1 + # An add-with-carry of zero effectively sets %rax to the carry flag. + adcq %rax, %rax + movq %rcx, 0(%rdi) + retq + +# CRYPTO_rdrand_multiple8_buf fills |len| bytes at |buf| with random data from +# the hardware RNG. The |len| argument must be a multiple of eight. It returns +# one on success and zero on hardware failure. +# int CRYPTO_rdrand_multiple8_buf(uint8_t *buf, size_t len); +.globl CRYPTO_rdrand_multiple8_buf +.type CRYPTO_rdrand_multiple8_buf,\@function,2 +.align 16 +CRYPTO_rdrand_multiple8_buf: + test %rsi, %rsi + jz .Lout + movq \$8, %rdx +.Lloop: + # This is rdrand %rcx. It sets rcx to a random value and sets the carry + # flag on success. + .byte 0x48, 0x0f, 0xc7, 0xf1 + jnc .Lerr + movq %rcx, 0(%rdi) + addq %rdx, %rdi + subq %rdx, %rsi + jnz .Lloop +.Lout: + movq \$1, %rax + retq +.Lerr: + xorq %rax, %rax retq ___ diff --git a/src/crypto/rand/hwrand.c b/src/crypto/rand/hwrand.c index 73d3de7..5f81f09 100644 --- a/src/crypto/rand/hwrand.c +++ b/src/crypto/rand/hwrand.c @@ -14,6 +14,7 @@ #include +#include #include #include @@ -26,21 +27,28 @@ int CRYPTO_have_hwrand(void) { return (OPENSSL_ia32cap_P[1] & (1u << 30)) != 0; } -/* CRYPTO_rdrand is defined in asm/rdrand-x86_64.pl */ -extern uint64_t CRYPTO_rdrand(void); +/* These functions are defined in asm/rdrand-x86_64.pl */ +extern int CRYPTO_rdrand(uint8_t out[8]); +extern int CRYPTO_rdrand_multiple8_buf(uint8_t *buf, size_t len); -void CRYPTO_hwrand(uint8_t *buf, size_t len) { - while (len >= 8) { - uint64_t rand = CRYPTO_rdrand(); - memcpy(buf, &rand, sizeof(rand)); - len -= sizeof(rand); - buf += sizeof(rand); +int CRYPTO_hwrand(uint8_t *buf, size_t len) { + const size_t len_multiple8 = len & ~7; + if (!CRYPTO_rdrand_multiple8_buf(buf, len_multiple8)) { + return 0; } + len -= len_multiple8; + + if (len != 0) { + assert(len < 8); - if (len > 0) { - uint64_t rand = CRYPTO_rdrand(); - memcpy(buf, &rand, len); + uint8_t rand_buf[8]; + if (!CRYPTO_rdrand(rand_buf)) { + return 0; + } + memcpy(buf + len_multiple8, rand_buf, len); } + + return 1; } #else diff --git a/src/crypto/rand/internal.h b/src/crypto/rand/internal.h index 1cca7f3..5e6ea11 100644 --- a/src/crypto/rand/internal.h +++ b/src/crypto/rand/internal.h @@ -29,8 +29,9 @@ void CRYPTO_sysrand(uint8_t *buf, size_t len); int CRYPTO_have_hwrand(void); /* CRYPTO_hwrand fills |len| bytes at |buf| with entropy from the hardware. - * This function can only be called if |CRYPTO_have_hwrand| returns one. */ -void CRYPTO_hwrand(uint8_t *buf, size_t len); + * This function can only be called if |CRYPTO_have_hwrand| returns one. + * It returns one on success or zero on hardware failure. */ +int CRYPTO_hwrand(uint8_t *buf, size_t len); #if defined(__cplusplus) diff --git a/src/crypto/rand/rand.c b/src/crypto/rand/rand.c index a647b6a..a96ac48 100644 --- a/src/crypto/rand/rand.c +++ b/src/crypto/rand/rand.c @@ -78,7 +78,8 @@ int RAND_bytes(uint8_t *buf, size_t len) { return 1; } - if (!CRYPTO_have_hwrand()) { + if (!CRYPTO_have_hwrand() || + !CRYPTO_hwrand(buf, len)) { /* Without a hardware RNG to save us from address-space duplication, the OS * entropy is used directly. */ CRYPTO_sysrand(buf, len); @@ -108,8 +109,6 @@ int RAND_bytes(uint8_t *buf, size_t len) { state->partial_block_used = sizeof(state->partial_block); } - CRYPTO_hwrand(buf, len); - if (len >= sizeof(state->partial_block)) { size_t remaining = len; while (remaining > 0) { diff --git a/win-x86_64/crypto/rand/rdrand-x86_64.asm b/win-x86_64/crypto/rand/rdrand-x86_64.asm index a63ea69..4c03791 100644 --- a/win-x86_64/crypto/rand/rdrand-x86_64.asm +++ b/win-x86_64/crypto/rand/rdrand-x86_64.asm @@ -5,6 +5,9 @@ default rel section .text code align=64 + + + global CRYPTO_rdrand ALIGN 16 @@ -16,7 +19,52 @@ $L$SEH_begin_CRYPTO_rdrand: mov rdi,rcx -DB 0x48,0x0f,0xc7,0xf0 + xor rax,rax + + +DB 0x48,0x0f,0xc7,0xf1 + + adc rax,rax + mov QWORD[rdi],rcx + mov rdi,QWORD[8+rsp] ;WIN64 epilogue + mov rsi,QWORD[16+rsp] + DB 0F3h,0C3h ;repret + + + + + +global CRYPTO_rdrand_multiple8_buf + +ALIGN 16 +CRYPTO_rdrand_multiple8_buf: + mov QWORD[8+rsp],rdi ;WIN64 prologue + mov QWORD[16+rsp],rsi + mov rax,rsp +$L$SEH_begin_CRYPTO_rdrand_multiple8_buf: + mov rdi,rcx + mov rsi,rdx + + + test rsi,rsi + jz NEAR $L$out + mov rdx,8 +$L$loop: + + +DB 0x48,0x0f,0xc7,0xf1 + jnc NEAR $L$err + mov QWORD[rdi],rcx + add rdi,rdx + sub rsi,rdx + jnz NEAR $L$loop +$L$out: + mov rax,1 + mov rdi,QWORD[8+rsp] ;WIN64 epilogue + mov rsi,QWORD[16+rsp] + DB 0F3h,0C3h ;repret +$L$err: + xor rax,rax mov rdi,QWORD[8+rsp] ;WIN64 epilogue mov rsi,QWORD[16+rsp] DB 0F3h,0C3h ;repret -- cgit v1.1