Add X509_V_FLAG_NO_CHECK_TIME.

This was added in OpenSSL 1.1.0. cryptography.io binds it. They don't
actually use it, but this is a useful feature to have anyway. Projects
like Envoy currently implement such a mode with
X509_STORE_set_verify_cb, which is a very problematic API to support.
Add this so we can move them to something more sustainable.

Change-Id: Iaff2d08daa743e0b5f4be261cb785fdcd26a8281
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/53965
Commit-Queue: Adam Langley <agl@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index 8343fee..58fdd26 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -1470,6 +1470,23 @@
             Verify(leaf.get(), {root.get()}, {root.get()},
                    {algorithm_mismatch_crl2.get()}, X509_V_FLAG_CRL_CHECK));
 
+  // The CRL is valid for a month.
+  EXPECT_EQ(X509_V_ERR_CRL_HAS_EXPIRED,
+            Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()},
+                   X509_V_FLAG_CRL_CHECK, [](X509_VERIFY_PARAM *param) {
+                     X509_VERIFY_PARAM_set_time(
+                         param, kReferenceTime + 2 * 30 * 24 * 3600);
+                   }));
+
+  // X509_V_FLAG_NO_CHECK_TIME suppresses the validity check.
+  EXPECT_EQ(X509_V_OK,
+            Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()},
+                   X509_V_FLAG_CRL_CHECK | X509_V_FLAG_NO_CHECK_TIME,
+                   [](X509_VERIFY_PARAM *param) {
+                     X509_VERIFY_PARAM_set_time(
+                         param, kReferenceTime + 2 * 30 * 24 * 3600);
+                   }));
+
   // Parsing kBadExtensionCRL should fail.
   EXPECT_FALSE(CRLFromPEM(kBadExtensionCRL));
 }
@@ -3566,6 +3583,95 @@
              }));
 }
 
+// Test that notBefore and notAfter checks work correctly.
+TEST(X509Test, Expiry) {
+  bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key);
+  ASSERT_TRUE(key);
+
+  // The following are measured in seconds relative to kReferenceTime. The
+  // validity periods are staggered so we can independently test both leaf and
+  // root time checks.
+  const time_t kSecondsInDay = 24 * 3600;
+  const time_t kRootStart = -30 * kSecondsInDay;
+  const time_t kIntermediateStart = -20 * kSecondsInDay;
+  const time_t kLeafStart = -10 * kSecondsInDay;
+  const time_t kIntermediateEnd = 10 * kSecondsInDay;
+  const time_t kLeafEnd = 20 * kSecondsInDay;
+  const time_t kRootEnd = 30 * kSecondsInDay;
+
+  bssl::UniquePtr<X509> root =
+      MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true);
+  ASSERT_TRUE(root);
+  ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notBefore(root.get()), kReferenceTime,
+                            /*offset_day=*/0,
+                            /*offset_sec=*/kRootStart));
+  ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notAfter(root.get()), kReferenceTime,
+                            /*offset_day=*/0,
+                            /*offset_sec=*/kRootEnd));
+  ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256()));
+
+  bssl::UniquePtr<X509> intermediate =
+      MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true);
+  ASSERT_TRUE(intermediate);
+  ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notBefore(intermediate.get()),
+                            kReferenceTime,
+                            /*offset_day=*/0,
+                            /*offset_sec=*/kIntermediateStart));
+  ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notAfter(intermediate.get()),
+                            kReferenceTime,
+                            /*offset_day=*/0,
+                            /*offset_sec=*/kIntermediateEnd));
+  ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256()));
+
+  bssl::UniquePtr<X509> leaf =
+      MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false);
+  ASSERT_TRUE(leaf);
+  ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notBefore(leaf.get()), kReferenceTime,
+                            /*offset_day=*/0,
+                            /*offset_sec=*/kLeafStart));
+  ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notAfter(leaf.get()), kReferenceTime,
+                            /*offset_day=*/0,
+                            /*offset_sec=*/kLeafEnd));
+  ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256()));
+
+  struct VerifyAt {
+    time_t time;
+    void operator()(X509_VERIFY_PARAM *param) const {
+      X509_VERIFY_PARAM_set_time(param, time);
+    }
+  };
+
+  for (bool check_time : {true, false}) {
+    SCOPED_TRACE(check_time);
+    unsigned long flags = check_time ? 0 : X509_V_FLAG_NO_CHECK_TIME;
+    int not_yet_valid = check_time ? X509_V_ERR_CERT_NOT_YET_VALID : X509_V_OK;
+    int has_expired = check_time ? X509_V_ERR_CERT_HAS_EXPIRED : X509_V_OK;
+
+    EXPECT_EQ(not_yet_valid,
+              Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags,
+                     VerifyAt{kReferenceTime + kRootStart - 1}));
+    EXPECT_EQ(not_yet_valid,
+              Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags,
+                     VerifyAt{kReferenceTime + kIntermediateStart - 1}));
+    EXPECT_EQ(not_yet_valid,
+              Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags,
+                     VerifyAt{kReferenceTime + kLeafStart - 1}));
+
+    EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()},
+                                {}, flags, VerifyAt{kReferenceTime}));
+
+    EXPECT_EQ(has_expired,
+              Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags,
+                     VerifyAt{kReferenceTime + kRootEnd + 1}));
+    EXPECT_EQ(has_expired,
+              Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags,
+                     VerifyAt{kReferenceTime + kIntermediateEnd + 1}));
+    EXPECT_EQ(has_expired,
+              Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, flags,
+                     VerifyAt{kReferenceTime + kLeafEnd + 1}));
+  }
+}
+
 // kConstructedBitString is an X.509 certificate where the signature is encoded
 // as a BER constructed BIT STRING. Note that, while OpenSSL's parser accepts
 // this input, it interprets the value incorrectly.
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index f6089cd..eca2d1e 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -971,18 +971,21 @@
 // Check CRL times against values in X509_STORE_CTX
 
 static int check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify) {
-  time_t *ptime;
-  int i;
+  if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) {
+    return 1;
+  }
+
   if (notify) {
     ctx->current_crl = crl;
   }
+  time_t *ptime;
   if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) {
     ptime = &ctx->param->check_time;
   } else {
     ptime = NULL;
   }
 
-  i = X509_cmp_time(X509_CRL_get0_lastUpdate(crl), ptime);
+  int i = X509_cmp_time(X509_CRL_get0_lastUpdate(crl), ptime);
   if (i == 0) {
     if (!notify) {
       return 0;
@@ -1739,16 +1742,18 @@
 }
 
 static int check_cert_time(X509_STORE_CTX *ctx, X509 *x) {
-  time_t *ptime;
-  int i;
+  if (ctx->param->flags & X509_V_FLAG_NO_CHECK_TIME) {
+    return 1;
+  }
 
+  time_t *ptime;
   if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) {
     ptime = &ctx->param->check_time;
   } else {
     ptime = NULL;
   }
 
-  i = X509_cmp_time(X509_get_notBefore(x), ptime);
+  int i = X509_cmp_time(X509_get_notBefore(x), ptime);
   if (i == 0) {
     ctx->error = X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD;
     ctx->current_cert = x;
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
index 96b7495..57eb002 100644
--- a/include/openssl/x509.h
+++ b/include/openssl/x509.h
@@ -2582,6 +2582,10 @@
 // will force the behaviour to match that of previous versions.
 #define X509_V_FLAG_NO_ALT_CHAINS 0x100000
 
+// X509_V_FLAG_NO_CHECK_TIME disables all time checks in certificate
+// verification.
+#define X509_V_FLAG_NO_CHECK_TIME 0x200000
+
 #define X509_VP_FLAG_DEFAULT 0x1
 #define X509_VP_FLAG_OVERWRITE 0x2
 #define X509_VP_FLAG_RESET_FLAGS 0x4