Do not depend on internals of the SSL state machine.
tlsdate has a "time_is_an_illusion" parameter which uses the server's
reported time (within some bounds) to check the certificate against. It
does this by configuring the time on the SSL's X509_VERIFY_PARAM when
one of the SSL3_ST_CR_SRVR_HELLO_A and SSL3_ST_CR_SRVR_HELLO_B states
passes.
In addition to depending on quirks of the OpenSSL state machine which
BoringSSL would otherwise need to emulate, this code is wrong. It needs
to run at a point after the server_random is filled in. In the original
OpenSSL code, SSL3_ST_CR_SRVR_HELLO_A is when the message header is
read, so this is too early. The _B also wouldn't work in a non-blocking
socket because state mcahine might pause halfway through reading the
body. This probably only worked because it only uses blocking BIOs.
This also depends on OpenSSL's info_callback hacking the state
transitions so SSL_state returned the previous state during the
callback.
Rather than ossify all these bugs, use SSL_CTX_set_cert_verify_callback.
This overrides OpenSSL's call to X509_verify_cert. By looking up the
server random immediately before verification, we are guaranteed
server_random is filled in. At this point we also have an X509_STORE_CTX
available, so we may set the time on it directly.
(Cherry-picked from
https://android.googlesource.com/platform/external/tlsdate/+/c339766a51d2db711171cb704e30b7ae916a987f%5E%21/)
BUG=chromium:736453
TEST=Deploy, reboot, tlsdate correctly sets date.
Change-Id: I8512e083a50be55fffaa5d2bbcb66f3badb4a4ee
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/tlsdate/+/2203402
Tested-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Commit-Queue: Jorge Lucangeli Obes <jorgelo@chromium.org>
Reviewed-by: David Benjamin <davidben@chromium.org>
Reviewed-by: Pavol Marko <pmarko@chromium.org>
diff --git a/src/tlsdate-helper.c b/src/tlsdate-helper.c
index fd0b3fe..476f301 100644
--- a/src/tlsdate-helper.c
+++ b/src/tlsdate-helper.c
@@ -322,45 +322,42 @@
}
-void
-openssl_time_callback (const SSL* ssl, int where, int ret)
+static int
+verify_with_server_time (X509_STORE_CTX *store_ctx, void *arg)
{
- if (where == SSL_CB_CONNECT_LOOP &&
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- (ssl->state == SSL3_ST_CR_SRVR_HELLO_A || ssl->state == SSL3_ST_CR_SRVR_HELLO_B))
-#else
- (SSL_get_state(ssl) == TLS_ST_CR_SRVR_HELLO))
-#endif
+ SSL *ssl = X509_STORE_CTX_get_ex_data (
+ store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+
+ // XXX TODO: If we want to trust the remote system for time,
+ // can we just read that time out of the remote system and if the
+ // cert verifies, decide that the time is reasonable?
+ // Such a process seems to indicate that a once valid cert would be
+ // forever valid - we stopgap that by ensuring it isn't less than
+ // the latest compiled_time and isn't above max_reasonable_time...
+ // XXX TODO: Solve eternal question about the Chicken and the Egg...
+ uint32_t compiled_time = RECENT_COMPILE_DATE;
+ uint32_t max_reasonable_time = MAX_REASONABLE_TIME;
+ uint32_t server_time;
+ verb ("V: freezing time for x509 verification\n");
+ SSL_get_server_random(ssl, (unsigned char *)&server_time,
+ sizeof (uint32_t));
+ if (compiled_time < ntohl (server_time)
+ &&
+ ntohl (server_time) < max_reasonable_time)
{
- // XXX TODO: If we want to trust the remote system for time,
- // can we just read that time out of the remote system and if the
- // cert verifies, decide that the time is reasonable?
- // Such a process seems to indicate that a once valid cert would be
- // forever valid - we stopgap that by ensuring it isn't less than
- // the latest compiled_time and isn't above max_reasonable_time...
- // XXX TODO: Solve eternal question about the Chicken and the Egg...
- uint32_t compiled_time = RECENT_COMPILE_DATE;
- uint32_t max_reasonable_time = MAX_REASONABLE_TIME;
- uint32_t server_time;
- verb ("V: freezing time for x509 verification\n");
- SSL_get_server_random(ssl, (unsigned char *)&server_time,
- sizeof (uint32_t));
- if (compiled_time < ntohl (server_time)
- &&
- ntohl (server_time) < max_reasonable_time)
- {
- verb ("V: remote peer provided: %d, preferred over compile time: %d\n",
- ntohl (server_time), compiled_time);
- verb ("V: freezing time with X509_VERIFY_PARAM_set_time\n");
- X509_VERIFY_PARAM_set_time (SSL_get0_param((SSL *)ssl),
- (time_t) ntohl (server_time) + 86400);
- }
- else
- {
- die ("V: the remote server is a false ticker! server: %d compile: %d\n",
- ntohl (server_time), compiled_time);
- }
+ verb ("V: remote peer provided: %d, preferred over compile time: %d\n",
+ ntohl (server_time), compiled_time);
+ verb ("V: freezing time with X509_VERIFY_PARAM_set_time\n");
+ X509_STORE_CTX_set_time (
+ store_ctx, 0, (time_t) ntohl (server_time) + 86400);
}
+ else
+ {
+ die ("V: the remote server is a false ticker! server: %d compile: %d\n",
+ ntohl (server_time), compiled_time);
+ }
+
+ return X509_verify_cert (store_ctx);
}
uint32_t
@@ -878,16 +875,18 @@
}
}
}
+
+ if (time_is_an_illusion)
+ {
+ SSL_CTX_set_cert_verify_callback (ctx, verify_with_server_time, NULL);
+ }
+
verb ("V: setting up connection to %s:%s\n", g_host, g_port);
if (NULL == (s_bio = make_ssl_bio(ctx, g_host, g_port, g_proxy)))
die ("SSL BIO setup failed\n");
BIO_get_ssl (s_bio, &ssl);
if (NULL == ssl)
die ("SSL setup failed\n");
- if (time_is_an_illusion)
- {
- SSL_set_info_callback (ssl, openssl_time_callback);
- }
SSL_set_mode (ssl, SSL_MODE_AUTO_RETRY);
SSL_set_tlsext_host_name (ssl, g_host);
if (NULL == BIO_new_fp (stdout, BIO_NOCLOSE))
diff --git a/src/tlsdate-helper.h b/src/tlsdate-helper.h
index 0e89b9a..d68c3be 100644
--- a/src/tlsdate-helper.h
+++ b/src/tlsdate-helper.h
@@ -79,7 +79,6 @@
// To support our RFC 2595 wildcard verification
#define RFC2595_MIN_LABEL_COUNT 3
-void openssl_time_callback (const SSL* ssl, int where, int ret);
uint32_t get_certificate_keybits (EVP_PKEY *public_key);
uint32_t check_cn (SSL *ssl, const char *hostname);
uint32_t check_san (SSL *ssl, const char *hostname);