Test that servers enforce session timeouts.

Extend the DTLS mock clock to apply to sessions too and test that
resumption behaves as expected.

Change-Id: Ib8fdec91b36e11cfa032872b63cf589f93b3da13
Reviewed-on: https://boringssl-review.googlesource.com/9110
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index 901168c..c3a9432 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -66,13 +66,6 @@
 
 #include "internal.h"
 
-#if defined(OPENSSL_WINDOWS)
-#include <sys/timeb.h>
-#else
-#include <sys/socket.h>
-#include <sys/time.h>
-#endif
-
 
 /* DTLS1_MTU_TIMEOUTS is the maximum number of timeouts to expire
  * before starting to decrease the MTU. */
@@ -82,8 +75,6 @@
  * before failing the DTLS handshake. */
 #define DTLS1_MAX_TIMEOUTS                     12
 
-static void get_current_time(const SSL *ssl, struct timeval *out_clock);
-
 int dtls1_new(SSL *ssl) {
   DTLS1_STATE *d1;
 
@@ -139,7 +130,7 @@
   }
 
   /* Set timeout to current time */
-  get_current_time(ssl, &ssl->d1->next_timeout);
+  ssl_get_current_time(ssl, &ssl->d1->next_timeout);
 
   /* Add duration to current time */
   ssl->d1->next_timeout.tv_sec += ssl->d1->timeout_duration_ms / 1000;
@@ -162,9 +153,8 @@
     return 0;
   }
 
-  /* Get current time */
   struct timeval timenow;
-  get_current_time(ssl, &timenow);
+  ssl_get_current_time(ssl, &timenow);
 
   /* If timer already expired, set remaining time to 0 */
   if (ssl->d1->next_timeout.tv_sec < timenow.tv_sec ||
@@ -273,22 +263,6 @@
   return dtls1_retransmit_outgoing_messages(ssl);
 }
 
-static void get_current_time(const SSL *ssl, struct timeval *out_clock) {
-  if (ssl->ctx->current_time_cb != NULL) {
-    ssl->ctx->current_time_cb(ssl, out_clock);
-    return;
-  }
-
-#if defined(OPENSSL_WINDOWS)
-  struct _timeb time;
-  _ftime(&time);
-  out_clock->tv_sec = time.time;
-  out_clock->tv_usec = time.millitm * 1000;
-#else
-  gettimeofday(out_clock, NULL);
-#endif
-}
-
 void dtls1_expect_flight(SSL *ssl) {
   dtls1_start_timer(ssl);
 }
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index 38fb3af..7135a8f 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -710,8 +710,10 @@
   if (ssl->session != NULL) {
     uint16_t session_version =
         ssl->method->version_from_wire(ssl->session->ssl_version);
+    struct timeval now;
+    ssl_get_current_time(ssl, &now);
     if (ssl->session->session_id_length == 0 || ssl->session->not_resumable ||
-        ssl->session->timeout < (long)(time(NULL) - ssl->session->time) ||
+        ssl->session->timeout < (long)now.tv_sec - ssl->session->time ||
         session_version < min_version || session_version > max_version) {
       SSL_set_session(ssl, NULL);
     }
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index 4d464d3..8a28c18 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -885,11 +885,12 @@
     ssl->s3->tlsext_channel_id_valid = 0;
   }
 
-  const uint32_t current_time = time(NULL);
-  ssl->s3->server_random[0] = current_time >> 24;
-  ssl->s3->server_random[1] = current_time >> 16;
-  ssl->s3->server_random[2] = current_time >> 8;
-  ssl->s3->server_random[3] = current_time;
+  struct timeval now;
+  ssl_get_current_time(ssl, &now);
+  ssl->s3->server_random[0] = now.tv_sec >> 24;
+  ssl->s3->server_random[1] = now.tv_sec >> 16;
+  ssl->s3->server_random[2] = now.tv_sec >> 8;
+  ssl->s3->server_random[3] = now.tv_sec;
   if (!RAND_bytes(ssl->s3->server_random + 4, SSL3_RANDOM_SIZE - 4)) {
     return -1;
   }
diff --git a/ssl/internal.h b/ssl/internal.h
index 5c8f32c..c69bc5f 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1479,6 +1479,8 @@
                             uint16_t signature_algorithm);
 void ssl_set_client_disabled(SSL *ssl);
 
+void ssl_get_current_time(const SSL *ssl, struct timeval *out_clock);
+
 
 #if defined(__cplusplus)
 } /* extern C */
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index a9df4e8..64fca64 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -155,6 +155,13 @@
 #include "internal.h"
 #include "../crypto/internal.h"
 
+#if defined(OPENSSL_WINDOWS)
+#include <sys/timeb.h>
+#else
+#include <sys/socket.h>
+#include <sys/time.h>
+#endif
+
 
 /* |SSL_R_UNKNOWN_PROTOCOL| is no longer emitted, but continue to define it
  * to avoid downstream churn. */
@@ -2102,7 +2109,9 @@
     CRYPTO_MUTEX_unlock_write(&ctx->lock);
 
     if (flush_cache) {
-      SSL_CTX_flush_sessions(ctx, (unsigned long)time(NULL));
+      struct timeval now;
+      ssl_get_current_time(ssl, &now);
+      SSL_CTX_flush_sessions(ctx, (long)now.tv_sec);
     }
   }
 }
@@ -3005,3 +3014,19 @@
   int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
   return SSL_set1_curves(ssl, &nid, 1);
 }
+
+void ssl_get_current_time(const SSL *ssl, struct timeval *out_clock) {
+  if (ssl->ctx->current_time_cb != NULL) {
+    ssl->ctx->current_time_cb(ssl, out_clock);
+    return;
+  }
+
+#if defined(OPENSSL_WINDOWS)
+  struct _timeb time;
+  _ftime(&time);
+  out_clock->tv_sec = time.time;
+  out_clock->tv_usec = time.millitm * 1000;
+#else
+  gettimeofday(out_clock, NULL);
+#endif
+}
diff --git a/ssl/ssl_session.c b/ssl/ssl_session.c
index 3bd52e0..0388661 100644
--- a/ssl/ssl_session.c
+++ b/ssl/ssl_session.c
@@ -170,7 +170,7 @@
   session->verify_result = X509_V_ERR_INVALID_CALL;
   session->references = 1;
   session->timeout = SSL_DEFAULT_SESSION_TIMEOUT;
-  session->time = (unsigned long)time(NULL);
+  session->time = (long)time(NULL);
   CRYPTO_new_ex_data(&session->ex_data);
   return session;
 }
@@ -419,6 +419,11 @@
     return 0;
   }
 
+  /* Fill in the time from the |SSL_CTX|'s clock. */
+  struct timeval now;
+  ssl_get_current_time(ssl, &now);
+  session->time = now.tv_sec;
+
   /* If the context has a default timeout, use it over the default. */
   if (ssl->initial_ctx->session_timeout != 0) {
     session->timeout = ssl->initial_ctx->session_timeout;
@@ -678,7 +683,9 @@
     return ssl_session_error;
   }
 
-  if (session->timeout < (long)(time(NULL) - session->time)) {
+  struct timeval now;
+  ssl_get_current_time(ssl, &now);
+  if (session->timeout < (long)now.tv_sec - session->time) {
     if (from_cache) {
       /* The session was from the cache, so remove it. */
       SSL_CTX_remove_session(ssl->initial_ctx, session);
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 97ade03..c2ce99b 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -34,6 +34,15 @@
 #include "test/scoped_types.h"
 #include "../crypto/test/test_util.h"
 
+#if defined(OPENSSL_WINDOWS)
+/* Windows defines struct timeval in winsock2.h. */
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
+#include <winsock2.h>
+OPENSSL_MSVC_PRAGMA(warning(pop))
+#else
+#include <sys/time.h>
+#endif
+
 
 struct ExpectedCipher {
   unsigned long id;
@@ -1840,6 +1849,71 @@
   return true;
 }
 
+static timeval g_current_time;
+
+static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) {
+  *out_clock = g_current_time;
+}
+
+static bool TestSessionTimeout() {
+  ScopedX509 cert = GetTestCertificate();
+  ScopedEVP_PKEY key = GetTestKey();
+  if (!cert || !key) {
+    return false;
+  }
+
+  for (uint16_t version : kVersions) {
+    // TODO(davidben): Enable this when TLS 1.3 resumption is implemented.
+    if (version == TLS1_3_VERSION) {
+      continue;
+    }
+
+    ScopedSSL_CTX server_ctx(SSL_CTX_new(TLS_method()));
+    ScopedSSL_CTX client_ctx(SSL_CTX_new(TLS_method()));
+    if (!server_ctx || !client_ctx ||
+        !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+        !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
+      return false;
+    }
+
+    SSL_CTX_set_min_version(client_ctx.get(), version);
+    SSL_CTX_set_max_version(client_ctx.get(), version);
+    SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH);
+
+    SSL_CTX_set_min_version(server_ctx.get(), version);
+    SSL_CTX_set_max_version(server_ctx.get(), version);
+    SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH);
+    SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback);
+
+    ScopedSSL_SESSION session =
+        CreateClientSession(client_ctx.get(), server_ctx.get());
+    if (!session) {
+      fprintf(stderr, "Error getting session (version = %04x).\n", version);
+      return false;
+    }
+
+    // Advance the clock just behind the timeout.
+    g_current_time.tv_sec += SSL_DEFAULT_SESSION_TIMEOUT;
+
+    if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+                             true /* expect session reused */)) {
+      fprintf(stderr, "Error resuming session (version = %04x).\n", version);
+      return false;
+    }
+
+    // Advance the clock one more second.
+    g_current_time.tv_sec++;
+
+    if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+                             false /* expect session not reused */)) {
+      fprintf(stderr, "Error resuming session (version = %04x).\n", version);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 int main() {
   CRYPTO_library_init();
 
@@ -1872,7 +1946,8 @@
       !TestGetPeerCertificate() ||
       !TestRetainOnlySHA256OfCerts() ||
       !TestClientHello() ||
-      !TestSessionIDContext()) {
+      !TestSessionIDContext() ||
+      !TestSessionTimeout()) {
     ERR_print_errors_fp(stderr);
     return 1;
   }
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 9cc1277..5694ac2 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -864,7 +864,9 @@
   SSL_CTX_enable_tls_channel_id(ssl_ctx.get());
   SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback);
 
-  SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
+  if (config->is_dtls) {
+    SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
+  }
 
   SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback);
   SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);