Ignore duplicates in |X509_STORE_add_*|

This change imports upstream's
https://github.com/openssl/openssl/commit/c0452248ea1a59a41023a4765ef7d9825e80a62b

Change-Id: Ib50ff9eb8c48d9580aa2ffcae92d3990cc987e30
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50905
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/err/x509.errordata b/crypto/err/x509.errordata
index ffa4267..65181bf 100644
--- a/crypto/err/x509.errordata
+++ b/crypto/err/x509.errordata
@@ -25,8 +25,11 @@
 X509,119,NEWER_CRL_NOT_NEWER
 X509,120,NOT_PKCS7_SIGNED_DATA
 X509,121,NO_CERTIFICATES_INCLUDED
+X509,141,NO_CERTIFICATE_FOUND
+X509,142,NO_CERTIFICATE_OR_CRL_FOUND
 X509,122,NO_CERT_SET_FOR_US_TO_VERIFY
 X509,123,NO_CRLS_INCLUDED
+X509,143,NO_CRL_FOUND
 X509,124,NO_CRL_NUMBER
 X509,125,PUBLIC_KEY_DECODE_ERROR
 X509,126,PUBLIC_KEY_ENCODE_ERROR
diff --git a/crypto/x509/by_dir.c b/crypto/x509/by_dir.c
index a630cdf..2f16944 100644
--- a/crypto/x509/by_dir.c
+++ b/crypto/x509/by_dir.c
@@ -438,6 +438,13 @@
                 ok = 1;
                 ret->type = tmp->type;
                 OPENSSL_memcpy(&ret->data, &tmp->data, sizeof(ret->data));
+
+                /*
+                 * Clear any errors that might have been raised processing empty
+                 * or malformed files.
+                 */
+                ERR_clear_error();
+
                 /*
                  * If we were going to up the reference count, we would need
                  * to do it on a perl 'type' basis
diff --git a/crypto/x509/by_file.c b/crypto/x509/by_file.c
index 1614c8c..0714b4f 100644
--- a/crypto/x509/by_file.c
+++ b/crypto/x509/by_file.c
@@ -126,8 +126,6 @@
     int i, count = 0;
     X509 *x = NULL;
 
-    if (file == NULL)
-        return (1);
     in = BIO_new(BIO_s_file());
 
     if ((in == NULL) || (BIO_read_filename(in, file) <= 0)) {
@@ -171,6 +169,11 @@
         OPENSSL_PUT_ERROR(X509, X509_R_BAD_X509_FILETYPE);
         goto err;
     }
+
+    if (ret == 0) {
+        OPENSSL_PUT_ERROR(X509, X509_R_NO_CERTIFICATE_FOUND);
+    }
+
  err:
     if (x != NULL)
         X509_free(x);
@@ -186,8 +189,6 @@
     int i, count = 0;
     X509_CRL *x = NULL;
 
-    if (file == NULL)
-        return (1);
     in = BIO_new(BIO_s_file());
 
     if ((in == NULL) || (BIO_read_filename(in, file) <= 0)) {
@@ -231,6 +232,11 @@
         OPENSSL_PUT_ERROR(X509, X509_R_BAD_X509_FILETYPE);
         goto err;
     }
+
+    if (ret == 0) {
+        OPENSSL_PUT_ERROR(X509, X509_R_NO_CRL_FOUND);
+    }
+
  err:
     if (x != NULL)
         X509_CRL_free(x);
@@ -246,6 +252,7 @@
     BIO *in;
     size_t i;
     int count = 0;
+
     if (type != X509_FILETYPE_PEM)
         return X509_load_cert_file(ctx, file, type);
     in = BIO_new_file(file, "r");
@@ -262,14 +269,24 @@
     for (i = 0; i < sk_X509_INFO_num(inf); i++) {
         itmp = sk_X509_INFO_value(inf, i);
         if (itmp->x509) {
-            X509_STORE_add_cert(ctx->store_ctx, itmp->x509);
+            if (!X509_STORE_add_cert(ctx->store_ctx, itmp->x509)) {
+                goto err;
+            }
             count++;
         }
         if (itmp->crl) {
-            X509_STORE_add_crl(ctx->store_ctx, itmp->crl);
+            if (!X509_STORE_add_crl(ctx->store_ctx, itmp->crl)) {
+                goto err;
+            }
             count++;
         }
     }
+
+    if (count == 0) {
+        OPENSSL_PUT_ERROR(X509, X509_R_NO_CERTIFICATE_OR_CRL_FOUND);
+    }
+
+err:
     sk_X509_INFO_pop_free(inf, X509_INFO_free);
     return count;
 }
diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c
index 041a5fd..4680e61 100644
--- a/crypto/x509/x509_lu.c
+++ b/crypto/x509/x509_lu.c
@@ -332,76 +332,54 @@
     return 1;
 }
 
-int X509_STORE_add_cert(X509_STORE *ctx, X509 *x)
+static int x509_store_add(X509_STORE *ctx, void *x, int is_crl)
 {
-    X509_OBJECT *obj;
-    int ret = 1;
-
-    if (x == NULL)
+    if (x == NULL) {
         return 0;
-    obj = (X509_OBJECT *)OPENSSL_malloc(sizeof(X509_OBJECT));
+    }
+
+    X509_OBJECT *const obj = (X509_OBJECT *)OPENSSL_malloc(sizeof(X509_OBJECT));
     if (obj == NULL) {
         OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
         return 0;
     }
-    obj->type = X509_LU_X509;
-    obj->data.x509 = x;
+
+    if (is_crl) {
+        obj->type = X509_LU_CRL;
+        obj->data.crl = (X509_CRL *)x;
+    } else {
+        obj->type = X509_LU_X509;
+        obj->data.x509 = (X509 *)x;
+    }
+    X509_OBJECT_up_ref_count(obj);
 
     CRYPTO_MUTEX_lock_write(&ctx->objs_lock);
 
-    X509_OBJECT_up_ref_count(obj);
-
-    if (X509_OBJECT_retrieve_match(ctx->objs, obj)) {
-        X509_OBJECT_free_contents(obj);
-        OPENSSL_free(obj);
-        OPENSSL_PUT_ERROR(X509, X509_R_CERT_ALREADY_IN_HASH_TABLE);
-        ret = 0;
-    } else if (!sk_X509_OBJECT_push(ctx->objs, obj)) {
-        X509_OBJECT_free_contents(obj);
-        OPENSSL_free(obj);
-        OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
-        ret = 0;
+    int ret = 1;
+    int added = 0;
+    /* Duplicates are silently ignored */
+    if (!X509_OBJECT_retrieve_match(ctx->objs, obj)) {
+        ret = added = (sk_X509_OBJECT_push(ctx->objs, obj) != 0);
     }
 
     CRYPTO_MUTEX_unlock_write(&ctx->objs_lock);
 
+    if (!added) {
+        X509_OBJECT_free_contents(obj);
+        OPENSSL_free(obj);
+    }
+
     return ret;
 }
 
+int X509_STORE_add_cert(X509_STORE *ctx, X509 *x)
+{
+    return x509_store_add(ctx, x, /*is_crl=*/0);
+}
+
 int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x)
 {
-    X509_OBJECT *obj;
-    int ret = 1;
-
-    if (x == NULL)
-        return 0;
-    obj = (X509_OBJECT *)OPENSSL_malloc(sizeof(X509_OBJECT));
-    if (obj == NULL) {
-        OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
-        return 0;
-    }
-    obj->type = X509_LU_CRL;
-    obj->data.crl = x;
-
-    CRYPTO_MUTEX_lock_write(&ctx->objs_lock);
-
-    X509_OBJECT_up_ref_count(obj);
-
-    if (X509_OBJECT_retrieve_match(ctx->objs, obj)) {
-        X509_OBJECT_free_contents(obj);
-        OPENSSL_free(obj);
-        OPENSSL_PUT_ERROR(X509, X509_R_CERT_ALREADY_IN_HASH_TABLE);
-        ret = 0;
-    } else if (!sk_X509_OBJECT_push(ctx->objs, obj)) {
-        X509_OBJECT_free_contents(obj);
-        OPENSSL_free(obj);
-        OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
-        ret = 0;
-    }
-
-    CRYPTO_MUTEX_unlock_write(&ctx->objs_lock);
-
-    return ret;
+    return x509_store_add(ctx, x, /*is_crl=*/1);
 }
 
 int X509_OBJECT_up_ref_count(X509_OBJECT *a)
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index 691b452..38414e9 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -3855,3 +3855,22 @@
     }
   }
 }
+
+TEST(X509Test, AddDuplicates) {
+  bssl::UniquePtr<X509_STORE> store(X509_STORE_new());
+  bssl::UniquePtr<X509> a(CertFromPEM(kCrossSigningRootPEM));
+  bssl::UniquePtr<X509> b(CertFromPEM(kRootCAPEM));
+
+  ASSERT_TRUE(store);
+  ASSERT_TRUE(a);
+  ASSERT_TRUE(b);
+
+  EXPECT_TRUE(X509_STORE_add_cert(store.get(), a.get()));
+  EXPECT_TRUE(X509_STORE_add_cert(store.get(), b.get()));
+  EXPECT_TRUE(X509_STORE_add_cert(store.get(), a.get()));
+  EXPECT_TRUE(X509_STORE_add_cert(store.get(), b.get()));
+  EXPECT_TRUE(X509_STORE_add_cert(store.get(), a.get()));
+  EXPECT_TRUE(X509_STORE_add_cert(store.get(), b.get()));
+
+  EXPECT_EQ(sk_X509_OBJECT_num(X509_STORE_get0_objects(store.get())), 2u);
+}
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
index 3c47789..6696988 100644
--- a/include/openssl/x509.h
+++ b/include/openssl/x509.h
@@ -2404,5 +2404,8 @@
 #define X509_R_DELTA_CRL_WITHOUT_CRL_NUMBER 138
 #define X509_R_INVALID_FIELD_FOR_VERSION 139
 #define X509_R_INVALID_VERSION 140
+#define X509_R_NO_CERTIFICATE_FOUND 141
+#define X509_R_NO_CERTIFICATE_OR_CRL_FOUND 142
+#define X509_R_NO_CRL_FOUND 143
 
 #endif