[openssl] master update

Richard Levitte levitte at openssl.org
Sun Nov 3 17:46:30 UTC 2019


The branch master has been updated
       via  bdb0e04fd0d8a797ecc367a522857dc8beec424d (commit)
       via  e90f08fb463bc2af537c588bfadf39ee4684ddeb (commit)
       via  849d91a62cede6787769389f40fc912db439bedb (commit)
       via  573e4bf0ba3daa9735b6f689407caa9d39ac4d0c (commit)
       via  f4aa6222bbd5955b37246155bc38ab1af94189b0 (commit)
       via  2897b00905afdb53458dde387729568342d8910c (commit)
       via  fd3397fc47bfd92e7e33d88aa566cb0c8bd29330 (commit)
       via  6dcb100f89d0ef081771d533fed342412ac7a13f (commit)
       via  e3c4ad283bd1a52f3d53de22e4fc6053bade14d6 (commit)
      from  3ee348b0dc5cd904fc2c022e6543f478c3d78732 (commit)


- Log -----------------------------------------------------------------
commit bdb0e04fd0d8a797ecc367a522857dc8beec424d
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Sep 2 07:59:17 2019 +0200

    Document added SSL functions related to X509_LOOKUP_store
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8442)

commit e90f08fb463bc2af537c588bfadf39ee4684ddeb
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Mar 8 23:43:19 2019 +0100

    X509_LOOKUP_store: Add CHANGES note
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8442)

commit 849d91a62cede6787769389f40fc912db439bedb
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Mar 8 23:41:27 2019 +0100

    Document X509_LOOKUP_store
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8442)

commit 573e4bf0ba3daa9735b6f689407caa9d39ac4d0c
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Mar 8 01:09:44 2019 +0100

    Adapt two test programs that were using now deprecated functions
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8442)

commit f4aa6222bbd5955b37246155bc38ab1af94189b0
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Mar 7 15:28:05 2019 +0100

    Add a basic test of -CAstore
    
    This code is mainly copied from test_ssl_old
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8442)

commit 2897b00905afdb53458dde387729568342d8910c
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Mar 7 15:27:15 2019 +0100

    OSSL_STORE: add tracing
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8442)

commit fd3397fc47bfd92e7e33d88aa566cb0c8bd29330
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Mar 7 15:26:34 2019 +0100

    Add -CAstore and similar to all openssl commands that have -CApath
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8442)

commit 6dcb100f89d0ef081771d533fed342412ac7a13f
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Mar 6 23:34:19 2019 +0100

    X509_LOOKUP_store: new X509_LOOKUP_METHOD that works by OSSL_STORE URI
    
    This is a wrapper around OSSL_STORE.
    
    This also adds necessary support functions:
    
    - X509_STORE_load_file
    - X509_STORE_load_path
    - X509_STORE_load_store
    - SSL_add_store_cert_subjects_to_stack
    - SSL_CTX_set_default_verify_store
    - SSL_CTX_load_verify_file
    - SSL_CTX_load_verify_dir
    - SSL_CTX_load_verify_store
    
    and deprecates X509_STORE_load_locations and SSL_CTX_load_verify_locations,
    as they aren't extensible.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8442)

commit e3c4ad283bd1a52f3d53de22e4fc6053bade14d6
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Mar 6 23:32:07 2019 +0100

    OSSL_STORE: constify the criterion parameter a bit more
    
    For some reason, OSSL_STORE_SEARCH_get0_name() and OSSL_STORE_find()
    accepted a non-const OSSL_STORE_SEARCH criterion, which isn't at all
    necessary.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/8442)

-----------------------------------------------------------------------

Summary of changes:
 CHANGES                                    |  24 +++
 apps/cms.c                                 |  19 ++-
 apps/crl.c                                 |  23 ++-
 apps/include/apps.h                        |  12 +-
 apps/include/s_apps.h                      |   5 +-
 apps/lib/apps.c                            |  38 ++++-
 apps/lib/opt.c                             |   5 +-
 apps/lib/s_cb.c                            |  18 ++-
 apps/ocsp.c                                |  19 ++-
 apps/pkcs12.c                              |  18 ++-
 apps/s_client.c                            |  43 ++++--
 apps/s_server.c                            |  42 +++++-
 apps/s_time.c                              |  24 ++-
 apps/smime.c                               |  19 ++-
 apps/ts.c                                  |  49 +++++--
 apps/verify.c                              |  25 +++-
 crypto/err/openssl.txt                     |   1 +
 crypto/store/loader_file.c                 |   3 +-
 crypto/store/store_lib.c                   |  22 ++-
 crypto/trace.c                             |   1 +
 crypto/x509/build.info                     |   2 +-
 crypto/x509/by_store.c                     | 227 +++++++++++++++++++++++++++++
 crypto/x509/x509_d2.c                      |  66 ++++++---
 doc/man1/openssl-cms.pod.in                |   4 +
 doc/man1/openssl-crl.pod.in                |   3 +-
 doc/man1/openssl-ocsp.pod.in               |  10 +-
 doc/man1/openssl-pkcs12.pod.in             |   3 +-
 doc/man1/openssl-s_client.pod.in           |   8 +-
 doc/man1/openssl-s_server.pod.in           |  15 +-
 doc/man1/openssl-s_time.pod.in             |   9 +-
 doc/man1/openssl-smime.pod.in              |   4 +
 doc/man1/openssl-ts.pod.in                 |   8 +-
 doc/man1/openssl-verify.pod.in             |  18 ++-
 doc/man1/openssl.pod                       |  17 +++
 doc/man3/SSL_CTX_load_verify_locations.pod |  55 +++++--
 doc/man3/X509_LOOKUP_hash_dir.pod          |  28 +++-
 doc/man3/X509_STORE_add_cert.pod           |  39 +++--
 doc/perlvars.pm                            |   7 +-
 include/openssl/ssl.h                      |  12 +-
 include/openssl/store.h                    |   6 +-
 include/openssl/trace.h                    |   3 +-
 include/openssl/x509_vfy.h                 |  16 +-
 ssl/ssl_cert.c                             |  74 ++++++++++
 ssl/ssl_conf.c                             |  54 ++++++-
 ssl/ssl_lib.c                              |  56 ++++++-
 test/danetest.c                            |   2 +-
 test/recipes/25-test_verify_store.t        | 118 +++++++++++++++
 test/ssltest_old.c                         |  15 +-
 util/libcrypto.num                         |   6 +-
 util/libssl.num                            |   7 +-
 util/missingmacro.txt                      |   2 +
 util/missingssl.txt                        |   1 +
 52 files changed, 1112 insertions(+), 193 deletions(-)
 create mode 100644 crypto/x509/by_store.c
 create mode 100644 test/recipes/25-test_verify_store.t

diff --git a/CHANGES b/CHANGES
index be4325e8bd..91c9bc7f06 100644
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,30 @@
 
  Changes between 1.1.1 and 3.0.0 [xx XXX xxxx]
 
+  *) Added the X509_LOOKUP_METHOD called X509_LOOKUP_store, to allow
+     access to certificate and CRL stores via URIs and OSSL_STORE
+     loaders.
+
+     This adds the following functions:
+
+     X509_LOOKUP_store()
+     X509_STORE_load_file()
+     X509_STORE_load_path()
+     X509_STORE_load_store()
+     SSL_add_store_cert_subjects_to_stack()
+     SSL_CTX_set_default_verify_store()
+     SSL_CTX_load_verify_file()
+     SSL_CTX_load_verify_dir()
+     SSL_CTX_load_verify_store()
+
+     Also, the following functions are now deprecated:
+
+     - X509_STORE_load_locations() (use X509_STORE_load_file(),
+       X509_STORE_load_path() or X509_STORE_load_store() instead)
+     - SSL_CTX_load_verify_locations() (use SSL_CTX_load_verify_file(),
+       SSL_CTX_load_verify_dir() or SSL_CTX_load_verify_store() instead)
+     [Richard Levitte]
+
   *) Added a new method to gather entropy on VMS, based on SYS$GET_ENTROPY.
      The presence of this system service is determined at run-time.
      [Richard Levitte]
diff --git a/apps/cms.c b/apps/cms.c
index 0e0df5e052..468820f3cf 100644
--- a/apps/cms.c
+++ b/apps/cms.c
@@ -75,7 +75,8 @@ typedef enum OPTION_choice {
     OPT_NOSIGS, OPT_NO_CONTENT_VERIFY, OPT_NO_ATTR_VERIFY, OPT_INDEF,
     OPT_NOINDEF, OPT_CRLFEOL, OPT_NOOUT, OPT_RR_PRINT,
     OPT_RR_ALL, OPT_RR_FIRST, OPT_RCTFORM, OPT_CERTFILE, OPT_CAFILE,
-    OPT_CAPATH, OPT_NOCAPATH, OPT_NOCAFILE,OPT_CONTENT, OPT_PRINT,
+    OPT_CAPATH, OPT_CASTORE, OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE,
+    OPT_CONTENT, OPT_PRINT,
     OPT_SECRETKEY, OPT_SECRETKEYID, OPT_PWRI_PASSWORD, OPT_ECONTENT_TYPE,
     OPT_PASSIN, OPT_TO, OPT_FROM, OPT_SUBJECT, OPT_SIGNER, OPT_RECIP,
     OPT_CERTSOUT, OPT_MD, OPT_INKEY, OPT_KEYFORM, OPT_KEYOPT, OPT_RR_FROM,
@@ -156,10 +157,13 @@ const OPTIONS cms_options[] = {
     {"certfile", OPT_CERTFILE, '<', "Other certificates file"},
     {"CAfile", OPT_CAFILE, '<', "Trusted certificates file"},
     {"CApath", OPT_CAPATH, '/', "trusted certificates directory"},
+    {"CAstore", OPT_CASTORE, ':', "trusted certificates store URI"},
     {"no-CAfile", OPT_NOCAFILE, '-',
      "Do not load the default certificates file"},
     {"no-CApath", OPT_NOCAPATH, '-',
      "Do not load certificates from the default certificates directory"},
+    {"no-CAstore", OPT_NOCASTORE, '-',
+     "Do not load certificates from the default certificates store"},
     {"content", OPT_CONTENT, '<',
      "Supply or override content for detached signature"},
     {"print", OPT_PRINT, '-',
@@ -219,9 +223,9 @@ int cms_main(int argc, char **argv)
     X509_STORE *store = NULL;
     X509_VERIFY_PARAM *vpm = NULL;
     char *certfile = NULL, *keyfile = NULL, *contfile = NULL;
-    const char *CAfile = NULL, *CApath = NULL;
+    const char *CAfile = NULL, *CApath = NULL, *CAstore = NULL;
     char *certsoutfile = NULL;
-    int noCAfile = 0, noCApath = 0;
+    int noCAfile = 0, noCApath = 0, noCAstore = 0;
     char *infile = NULL, *outfile = NULL, *rctfile = NULL;
     char *passinarg = NULL, *passin = NULL, *signerfile = NULL, *recipfile = NULL;
     char *to = NULL, *from = NULL, *subject = NULL, *prog;
@@ -401,12 +405,18 @@ int cms_main(int argc, char **argv)
         case OPT_CAPATH:
             CApath = opt_arg();
             break;
+        case OPT_CASTORE:
+            CAstore = opt_arg();
+            break;
         case OPT_NOCAFILE:
             noCAfile = 1;
             break;
         case OPT_NOCAPATH:
             noCApath = 1;
             break;
+        case OPT_NOCASTORE:
+            noCAstore = 1;
+            break;
         case OPT_IN:
             infile = opt_arg();
             break;
@@ -825,7 +835,8 @@ int cms_main(int argc, char **argv)
         goto end;
 
     if ((operation == SMIME_VERIFY) || (operation == SMIME_VERIFY_RECEIPT)) {
-        if ((store = setup_verify(CAfile, CApath, noCAfile, noCApath)) == NULL)
+        if ((store = setup_verify(CAfile, noCAfile, CApath, noCApath,
+                                  CAstore, noCAstore)) == NULL)
             goto end;
         X509_STORE_set_verify_cb(store, cms_cb);
         if (vpmtouched)
diff --git a/apps/crl.c b/apps/crl.c
index d36b93ba64..f7f4fb7150 100644
--- a/apps/crl.c
+++ b/apps/crl.c
@@ -22,9 +22,9 @@ typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_INFORM, OPT_IN, OPT_OUTFORM, OPT_OUT, OPT_KEYFORM, OPT_KEY,
     OPT_ISSUER, OPT_LASTUPDATE, OPT_NEXTUPDATE, OPT_FINGERPRINT,
-    OPT_CRLNUMBER, OPT_BADSIG, OPT_GENDELTA, OPT_CAPATH, OPT_CAFILE,
-    OPT_NOCAPATH, OPT_NOCAFILE, OPT_VERIFY, OPT_TEXT, OPT_HASH, OPT_HASH_OLD,
-    OPT_NOOUT, OPT_NAMEOPT, OPT_MD
+    OPT_CRLNUMBER, OPT_BADSIG, OPT_GENDELTA, OPT_CAPATH, OPT_CAFILE, OPT_CASTORE,
+    OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE, OPT_VERIFY, OPT_TEXT, OPT_HASH,
+    OPT_HASH_OLD, OPT_NOOUT, OPT_NAMEOPT, OPT_MD
 } OPTION_CHOICE;
 
 const OPTIONS crl_options[] = {
@@ -45,10 +45,13 @@ const OPTIONS crl_options[] = {
     {"gendelta", OPT_GENDELTA, '<', "Other CRL to compare/diff to the Input one"},
     {"CApath", OPT_CAPATH, '/', "Verify CRL using certificates in dir"},
     {"CAfile", OPT_CAFILE, '<', "Verify CRL using certificates in file name"},
+    {"CAstore", OPT_CASTORE, ':', "Verify CRL using certificates in store URI"},
     {"no-CAfile", OPT_NOCAFILE, '-',
      "Do not load the default certificates file"},
     {"no-CApath", OPT_NOCAPATH, '-',
      "Do not load certificates from the default certificates directory"},
+    {"no-CAstore", OPT_NOCASTORE, '-',
+     "Do not load certificates from the default certificates store"},
     {"verify", OPT_VERIFY, '-', "Verify CRL signature"},
     {"text", OPT_TEXT, '-', "Print out a text format version"},
     {"hash", OPT_HASH, '-', "Print hash value"},
@@ -71,12 +74,12 @@ int crl_main(int argc, char **argv)
     EVP_PKEY *pkey;
     const EVP_MD *digest = EVP_sha1();
     char *infile = NULL, *outfile = NULL, *crldiff = NULL, *keyfile = NULL;
-    const char *CAfile = NULL, *CApath = NULL, *prog;
+    const char *CAfile = NULL, *CApath = NULL, *CAstore = NULL, *prog;
     OPTION_CHOICE o;
     int hash = 0, issuer = 0, lastupdate = 0, nextupdate = 0, noout = 0;
     int informat = FORMAT_PEM, outformat = FORMAT_PEM, keyformat = FORMAT_PEM;
     int ret = 1, num = 0, badsig = 0, fingerprint = 0, crlnumber = 0;
-    int text = 0, do_ver = 0, noCAfile = 0, noCApath = 0;
+    int text = 0, do_ver = 0, noCAfile = 0, noCApath = 0, noCAstore = 0;
     int i;
 #ifndef OPENSSL_NO_MD5
     int hash_old = 0;
@@ -126,12 +129,19 @@ int crl_main(int argc, char **argv)
             CAfile = opt_arg();
             do_ver = 1;
             break;
+        case OPT_CASTORE:
+            CAstore = opt_arg();
+            do_ver = 1;
+            break;
         case OPT_NOCAPATH:
             noCApath =  1;
             break;
         case OPT_NOCAFILE:
             noCAfile =  1;
             break;
+        case OPT_NOCASTORE:
+            noCAstore =  1;
+            break;
         case OPT_HASH_OLD:
 #ifndef OPENSSL_NO_MD5
             hash_old = ++num;
@@ -185,7 +195,8 @@ int crl_main(int argc, char **argv)
         goto end;
 
     if (do_ver) {
-        if ((store = setup_verify(CAfile, CApath, noCAfile, noCApath)) == NULL)
+        if ((store = setup_verify(CAfile, noCAfile, CApath, noCApath,
+                                  CAstore, noCAstore)) == NULL)
             goto end;
         lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
         if (lookup == NULL)
diff --git a/apps/include/apps.h b/apps/include/apps.h
index 41db80740b..21a2a90544 100644
--- a/apps/include/apps.h
+++ b/apps/include/apps.h
@@ -126,11 +126,13 @@ int load_certs(const char *file, STACK_OF(X509) **certs, int format,
                const char *pass, const char *cert_descrip);
 int load_crls(const char *file, STACK_OF(X509_CRL) **crls, int format,
               const char *pass, const char *cert_descrip);
-X509_STORE *setup_verify(const char *CAfile, const char *CApath,
-                         int noCAfile, int noCApath);
-__owur int ctx_set_verify_locations(SSL_CTX *ctx, const char *CAfile,
-                                    const char *CApath, int noCAfile,
-                                    int noCApath);
+X509_STORE *setup_verify(const char *CAfile, int noCAfile,
+                         const char *CApath, int noCApath,
+                         const char *CAstore, int noCAstore);
+__owur int ctx_set_verify_locations(SSL_CTX *ctx,
+                                    const char *CAfile, int noCAfile,
+                                    const char *CApath, int noCApath,
+                                    const char *CAstore, int noCAstore);
 
 #ifndef OPENSSL_NO_CT
 
diff --git a/apps/include/s_apps.h b/apps/include/s_apps.h
index 4f976da0b2..1bbe5fe09d 100644
--- a/apps/include/s_apps.h
+++ b/apps/include/s_apps.h
@@ -69,8 +69,9 @@ int config_ctx(SSL_CONF_CTX *cctx, STACK_OF(OPENSSL_STRING) *str, SSL_CTX *ctx);
 int ssl_ctx_add_crls(SSL_CTX *ctx, STACK_OF(X509_CRL) *crls,
                      int crl_download);
 int ssl_load_stores(SSL_CTX *ctx, const char *vfyCApath,
-                    const char *vfyCAfile, const char *chCApath,
-                    const char *chCAfile, STACK_OF(X509_CRL) *crls,
+                    const char *vfyCAfile, const char *vfyCAstore,
+                    const char *chCApath, const char *chCAfile,
+                    const char *chCAstore, STACK_OF(X509_CRL) *crls,
                     int crl_download);
 void ssl_ctx_security_debug(SSL_CTX *ctx, int verbose);
 int set_keylog_file(SSL_CTX *ctx, const char *keylog_file);
diff --git a/apps/lib/apps.c b/apps/lib/apps.c
index 73483d99f4..8b840bb2a1 100644
--- a/apps/lib/apps.c
+++ b/apps/lib/apps.c
@@ -125,18 +125,29 @@ int app_init(long mesgwin)
 }
 #endif
 
-int ctx_set_verify_locations(SSL_CTX *ctx, const char *CAfile,
-                             const char *CApath, int noCAfile, int noCApath)
+int ctx_set_verify_locations(SSL_CTX *ctx,
+                             const char *CAfile, int noCAfile,
+                             const char *CApath, int noCApath,
+                             const char *CAstore, int noCAstore)
 {
-    if (CAfile == NULL && CApath == NULL) {
+    if (CAfile == NULL && CApath == NULL && CAstore == NULL) {
         if (!noCAfile && SSL_CTX_set_default_verify_file(ctx) <= 0)
             return 0;
         if (!noCApath && SSL_CTX_set_default_verify_dir(ctx) <= 0)
             return 0;
+        if (!noCAstore && SSL_CTX_set_default_verify_store(ctx) <= 0)
+            return 0;
 
         return 1;
     }
-    return SSL_CTX_load_verify_locations(ctx, CAfile, CApath);
+
+    if (CAfile != NULL && !SSL_CTX_load_verify_file(ctx, CAfile))
+        return 0;
+    if (CApath != NULL && !SSL_CTX_load_verify_dir(ctx, CApath))
+        return 0;
+    if (CAstore != NULL && !SSL_CTX_load_verify_store(ctx, CAstore))
+        return 0;
+    return 1;
 }
 
 #ifndef OPENSSL_NO_CT
@@ -1068,7 +1079,9 @@ void print_array(BIO *out, const char* title, int len, const unsigned char* d)
     BIO_printf(out, "\n};\n");
 }
 
-X509_STORE *setup_verify(const char *CAfile, const char *CApath, int noCAfile, int noCApath)
+X509_STORE *setup_verify(const char *CAfile, int noCAfile,
+                         const char *CApath, int noCApath,
+                         const char *CAstore, int noCAstore)
 {
     X509_STORE *store = X509_STORE_new();
     X509_LOOKUP *lookup;
@@ -1080,7 +1093,7 @@ X509_STORE *setup_verify(const char *CAfile, const char *CApath, int noCAfile, i
         lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
         if (lookup == NULL)
             goto end;
-        if (CAfile) {
+        if (CAfile != NULL) {
             if (!X509_LOOKUP_load_file(lookup, CAfile, X509_FILETYPE_PEM)) {
                 BIO_printf(bio_err, "Error loading file %s\n", CAfile);
                 goto end;
@@ -1094,7 +1107,7 @@ X509_STORE *setup_verify(const char *CAfile, const char *CApath, int noCAfile, i
         lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
         if (lookup == NULL)
             goto end;
-        if (CApath) {
+        if (CApath != NULL) {
             if (!X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM)) {
                 BIO_printf(bio_err, "Error loading directory %s\n", CApath);
                 goto end;
@@ -1104,6 +1117,17 @@ X509_STORE *setup_verify(const char *CAfile, const char *CApath, int noCAfile, i
         }
     }
 
+    if (CAstore != NULL || !noCAstore) {
+        lookup = X509_STORE_add_lookup(store, X509_LOOKUP_store());
+        if (lookup == NULL)
+            goto end;
+        if (!X509_LOOKUP_add_store(lookup, CAstore)) {
+            if (CAstore != NULL)
+                BIO_printf(bio_err, "Error loading store URI %s\n", CAstore);
+            goto end;
+        }
+    }
+
     ERR_clear_error();
     return store;
  end:
diff --git a/apps/lib/opt.c b/apps/lib/opt.c
index 44d2570ae7..98979fc2a1 100644
--- a/apps/lib/opt.c
+++ b/apps/lib/opt.c
@@ -146,7 +146,7 @@ char *opt_init(int ac, char **av, const OPTIONS *o)
         switch (i) {
         case   0: case '-': case '/': case '<': case '>': case 'E': case 'F':
         case 'M': case 'U': case 'f': case 'l': case 'n': case 'p': case 's':
-        case 'u': case 'c':
+        case 'u': case 'c': case ':':
             break;
         default:
             OPENSSL_assert(0);
@@ -686,6 +686,7 @@ int opt_next(void)
         switch (o->valtype) {
         default:
         case 's':
+        case ':':
             /* Just a string. */
             break;
         case '/':
@@ -804,6 +805,8 @@ static const char *valtype2param(const OPTIONS *o)
     case 0:
     case '-':
         return "";
+    case ':':
+        return "uri";
     case 's':
         return "val";
     case '/':
diff --git a/apps/lib/s_cb.c b/apps/lib/s_cb.c
index 47b8afe9ef..7b81d60fe7 100644
--- a/apps/lib/s_cb.c
+++ b/apps/lib/s_cb.c
@@ -1262,27 +1262,37 @@ int ssl_ctx_add_crls(SSL_CTX *ctx, STACK_OF(X509_CRL) *crls, int crl_download)
 
 int ssl_load_stores(SSL_CTX *ctx,
                     const char *vfyCApath, const char *vfyCAfile,
+                    const char *vfyCAstore,
                     const char *chCApath, const char *chCAfile,
+                    const char *chCAstore,
                     STACK_OF(X509_CRL) *crls, int crl_download)
 {
     X509_STORE *vfy = NULL, *ch = NULL;
     int rv = 0;
-    if (vfyCApath != NULL || vfyCAfile != NULL) {
+    if (vfyCApath != NULL || vfyCAfile != NULL || vfyCAstore != NULL) {
         vfy = X509_STORE_new();
         if (vfy == NULL)
             goto err;
-        if (!X509_STORE_load_locations(vfy, vfyCAfile, vfyCApath))
+        if (vfyCAfile != NULL && !X509_STORE_load_file(vfy, vfyCAfile))
+            goto err;
+        if (vfyCApath != NULL && !X509_STORE_load_path(vfy, vfyCApath))
+            goto err;
+        if (vfyCAstore != NULL && !X509_STORE_load_store(vfy, vfyCAstore))
             goto err;
         add_crls_store(vfy, crls);
         SSL_CTX_set1_verify_cert_store(ctx, vfy);
         if (crl_download)
             store_setup_crl_download(vfy);
     }
-    if (chCApath != NULL || chCAfile != NULL) {
+    if (chCApath != NULL || chCAfile != NULL || chCAstore != NULL) {
         ch = X509_STORE_new();
         if (ch == NULL)
             goto err;
-        if (!X509_STORE_load_locations(ch, chCAfile, chCApath))
+        if (chCAfile != NULL && !X509_STORE_load_file(ch, chCAfile))
+            goto err;
+        if (chCApath != NULL && !X509_STORE_load_path(ch, chCApath))
+            goto err;
+        if (chCAstore != NULL && !X509_STORE_load_store(ch, chCAstore))
             goto err;
         SSL_CTX_set1_chain_cert_store(ctx, ch);
     }
diff --git a/apps/ocsp.c b/apps/ocsp.c
index 71c6a56f2f..458c808634 100644
--- a/apps/ocsp.c
+++ b/apps/ocsp.c
@@ -134,7 +134,8 @@ typedef enum OPTION_choice {
     OPT_NO_CERT_CHECKS, OPT_NO_EXPLICIT, OPT_TRUST_OTHER,
     OPT_NO_INTERN, OPT_BADSIG, OPT_TEXT, OPT_REQ_TEXT, OPT_RESP_TEXT,
     OPT_REQIN, OPT_RESPIN, OPT_SIGNER, OPT_VAFILE, OPT_SIGN_OTHER,
-    OPT_VERIFY_OTHER, OPT_CAFILE, OPT_CAPATH, OPT_NOCAFILE, OPT_NOCAPATH,
+    OPT_VERIFY_OTHER, OPT_CAFILE, OPT_CAPATH, OPT_CASTORE, OPT_NOCAFILE,
+    OPT_NOCAPATH, OPT_NOCASTORE,
     OPT_VALIDITY_PERIOD, OPT_STATUS_AGE, OPT_SIGNKEY, OPT_REQOUT,
     OPT_RESPOUT, OPT_PATH, OPT_ISSUER, OPT_CERT, OPT_SERIAL,
     OPT_INDEX, OPT_CA, OPT_NMIN, OPT_REQUEST, OPT_NDAYS, OPT_RSIGNER,
@@ -195,10 +196,13 @@ const OPTIONS ocsp_options[] = {
      "Additional certificates to search for signer"},
     {"CAfile", OPT_CAFILE, '<', "Trusted certificates file"},
     {"CApath", OPT_CAPATH, '<', "Trusted certificates directory"},
+    {"CAstore", OPT_CASTORE, ':', "Trusted certificates store URI"},
     {"no-CAfile", OPT_NOCAFILE, '-',
      "Do not load the default certificates file"},
     {"no-CApath", OPT_NOCAPATH, '-',
      "Do not load certificates from the default certificates directory"},
+    {"no-CAstore", OPT_NOCAPATH, '-',
+     "Do not load certificates from the default certificates store"},
     {"validity_period", OPT_VALIDITY_PERIOD, 'u',
      "Maximum validity discrepancy in seconds"},
     {"status_age", OPT_STATUS_AGE, 'p', "Maximum status age in seconds"},
@@ -250,7 +254,7 @@ int ocsp_main(int argc, char **argv)
     X509 *signer = NULL, *rsigner = NULL;
     X509_STORE *store = NULL;
     X509_VERIFY_PARAM *vpm = NULL;
-    const char *CAfile = NULL, *CApath = NULL;
+    const char *CAfile = NULL, *CApath = NULL, *CAstore = NULL;
     char *header, *value;
     char *host = NULL, *port = NULL, *path = "/", *outfile = NULL;
     char *rca_filename = NULL, *reqin = NULL, *respin = NULL;
@@ -259,7 +263,7 @@ int ocsp_main(int argc, char **argv)
     char *sign_certfile = NULL, *verify_certfile = NULL, *rcertfile = NULL;
     char *signfile = NULL, *keyfile = NULL;
     char *thost = NULL, *tport = NULL, *tpath = NULL;
-    int noCAfile = 0, noCApath = 0;
+    int noCAfile = 0, noCApath = 0, noCAstore = 0;
     int accept_count = -1, add_nonce = 1, noverify = 0, use_ssl = -1;
     int vpmtouched = 0, badsig = 0, i, ignore_err = 0, nmin = 0, ndays = -1;
     int req_text = 0, resp_text = 0, ret = 1;
@@ -395,12 +399,18 @@ int ocsp_main(int argc, char **argv)
         case OPT_CAPATH:
             CApath = opt_arg();
             break;
+        case OPT_CASTORE:
+            CAstore = opt_arg();
+            break;
         case OPT_NOCAFILE:
             noCAfile = 1;
             break;
         case OPT_NOCAPATH:
             noCApath = 1;
             break;
+        case OPT_NOCASTORE:
+            noCAstore = 1;
+            break;
         case OPT_V_CASES:
             if (!opt_verify(o, vpm))
                 goto end;
@@ -765,7 +775,8 @@ redo_accept:
     }
 
     if (store == NULL) {
-        store = setup_verify(CAfile, CApath, noCAfile, noCApath);
+        store = setup_verify(CAfile, noCAfile, CApath, noCApath,
+                             CAstore, noCAstore);
         if (!store)
             goto end;
     }
diff --git a/apps/pkcs12.c b/apps/pkcs12.c
index a708064db1..781c3ad7ec 100644
--- a/apps/pkcs12.c
+++ b/apps/pkcs12.c
@@ -57,7 +57,7 @@ typedef enum OPTION_choice {
     OPT_NOMAC, OPT_LMK, OPT_NODES, OPT_MACALG, OPT_CERTPBE, OPT_KEYPBE,
     OPT_INKEY, OPT_CERTFILE, OPT_NAME, OPT_CSP, OPT_CANAME,
     OPT_IN, OPT_OUT, OPT_PASSIN, OPT_PASSOUT, OPT_PASSWORD, OPT_CAPATH,
-    OPT_CAFILE, OPT_NOCAPATH, OPT_NOCAFILE, OPT_ENGINE,
+    OPT_CAFILE, OPT_CASTORE, OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE, OPT_ENGINE,
     OPT_R_ENUM
 } OPTION_CHOICE;
 
@@ -108,10 +108,13 @@ const OPTIONS pkcs12_options[] = {
     {"password", OPT_PASSWORD, 's', "Set import/export password source"},
     {"CApath", OPT_CAPATH, '/', "PEM-format directory of CA's"},
     {"CAfile", OPT_CAFILE, '<', "PEM-format file of CA's"},
+    {"CAstore", OPT_CASTORE, ':', "URI to store if CA's"},
     {"no-CAfile", OPT_NOCAFILE, '-',
      "Do not load the default certificates file"},
     {"no-CApath", OPT_NOCAPATH, '-',
      "Do not load certificates from the default certificates directory"},
+    {"no-CAstore", OPT_NOCASTORE, '-',
+     "Do not load certificates from the default certificates store"},
     {"", OPT_CIPHER, '-', "Any supported cipher"},
 # ifndef OPENSSL_NO_ENGINE
     {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
@@ -137,8 +140,8 @@ int pkcs12_main(int argc, char **argv)
     char *passinarg = NULL, *passoutarg = NULL, *passarg = NULL;
     char *passin = NULL, *passout = NULL, *macalg = NULL;
     char *cpass = NULL, *mpass = NULL, *badpass = NULL;
-    const char *CApath = NULL, *CAfile = NULL, *prog;
-    int noCApath = 0, noCAfile = 0;
+    const char *CApath = NULL, *CAfile = NULL, *CAstore = NULL, *prog;
+    int noCApath = 0, noCAfile = 0, noCAstore = 0;
     ENGINE *e = NULL;
     BIO *in = NULL, *out = NULL;
     PKCS12 *p12 = NULL;
@@ -270,12 +273,18 @@ int pkcs12_main(int argc, char **argv)
         case OPT_CAPATH:
             CApath = opt_arg();
             break;
+        case OPT_CASTORE:
+            CAstore = opt_arg();
+            break;
         case OPT_CAFILE:
             CAfile = opt_arg();
             break;
         case OPT_NOCAPATH:
             noCApath = 1;
             break;
+        case OPT_NOCASTORE:
+            noCAstore = 1;
+            break;
         case OPT_NOCAFILE:
             noCAfile = 1;
             break;
@@ -404,7 +413,8 @@ int pkcs12_main(int argc, char **argv)
             int vret;
             STACK_OF(X509) *chain2;
             X509_STORE *store;
-            if ((store = setup_verify(CAfile, CApath, noCAfile, noCApath))
+            if ((store = setup_verify(CAfile, noCAfile, CApath, noCApath,
+                                      CAstore, noCAstore))
                     == NULL)
                 goto export_end;
 
diff --git a/apps/s_client.c b/apps/s_client.c
index 392ab02234..fa5cb95f68 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -581,9 +581,11 @@ typedef enum OPTION_choice {
     OPT_SSL3, OPT_SSL_CONFIG,
     OPT_TLS1_3, OPT_TLS1_2, OPT_TLS1_1, OPT_TLS1, OPT_DTLS, OPT_DTLS1,
     OPT_DTLS1_2, OPT_SCTP, OPT_TIMEOUT, OPT_MTU, OPT_KEYFORM, OPT_PASS,
-    OPT_CERT_CHAIN, OPT_CAPATH, OPT_NOCAPATH, OPT_CHAINCAPATH, OPT_VERIFYCAPATH,
-    OPT_KEY, OPT_RECONNECT, OPT_BUILD_CHAIN, OPT_CAFILE, OPT_NOCAFILE,
-    OPT_CHAINCAFILE, OPT_VERIFYCAFILE, OPT_NEXTPROTONEG, OPT_ALPN,
+    OPT_CERT_CHAIN, OPT_KEY, OPT_RECONNECT, OPT_BUILD_CHAIN,
+    OPT_NEXTPROTONEG, OPT_ALPN,
+    OPT_CAPATH, OPT_NOCAPATH, OPT_CHAINCAPATH, OPT_VERIFYCAPATH,
+    OPT_CAFILE, OPT_NOCAFILE, OPT_CHAINCAFILE, OPT_VERIFYCAFILE,
+    OPT_CASTORE, OPT_NOCASTORE, OPT_CHAINCASTORE, OPT_VERIFYCASTORE,
     OPT_SERVERINFO, OPT_STARTTLS, OPT_SERVERNAME, OPT_NOSERVERNAME, OPT_ASYNC,
     OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_PROTOHOST,
     OPT_MAXFRAGLEN, OPT_MAX_SEND_FRAG, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES,
@@ -630,10 +632,13 @@ const OPTIONS s_client_options[] = {
     {"pass", OPT_PASS, 's', "Private key file pass phrase source"},
     {"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"},
     {"CAfile", OPT_CAFILE, '<', "PEM format file of CA's"},
+    {"CAstore", OPT_CAFILE, ':', "URI to store of CA's"},
     {"no-CAfile", OPT_NOCAFILE, '-',
      "Do not load the default certificates file"},
     {"no-CApath", OPT_NOCAPATH, '-',
      "Do not load certificates from the default certificates directory"},
+    {"no-CAstore", OPT_NOCAPATH, '-',
+     "Do not load certificates from the default certificates store"},
     {"requestCAfile", OPT_REQCAFILE, '<',
       "PEM format file of CA names to send to the server"},
     {"dane_tlsa_domain", OPT_DANE_TLSA_DOMAIN, 's', "DANE TLSA base domain"},
@@ -700,6 +705,10 @@ const OPTIONS s_client_options[] = {
      "CA file for certificate chain (PEM format)"},
     {"verifyCAfile", OPT_VERIFYCAFILE, '<',
      "CA file for certificate verification (PEM format)"},
+    {"chainCAstore", OPT_CHAINCASTORE, ':',
+     "CA store URI for certificate chain"},
+    {"verifyCAstore", OPT_VERIFYCASTORE, ':',
+     "CA store URI for certificate verification"},
     {"nocommands", OPT_NOCMDS, '-', "Do not use interactive command letters"},
     {"servername", OPT_SERVERNAME, 's',
      "Set TLS extension servername (SNI) in ClientHello (default)"},
@@ -899,22 +908,23 @@ int s_client_main(int argc, char **argv)
     int dane_ee_no_name = 0;
     STACK_OF(X509_CRL) *crls = NULL;
     const SSL_METHOD *meth = TLS_client_method();
-    const char *CApath = NULL, *CAfile = NULL;
+    const char *CApath = NULL, *CAfile = NULL, *CAstore = NULL;
     char *cbuf = NULL, *sbuf = NULL, *mbuf = NULL;
     char *proxystr = NULL, *proxyuser = NULL;
     char *proxypassarg = NULL, *proxypass = NULL;
     char *connectstr = NULL, *bindstr = NULL;
     char *cert_file = NULL, *key_file = NULL, *chain_file = NULL;
-    char *chCApath = NULL, *chCAfile = NULL, *host = NULL;
+    char *chCApath = NULL, *chCAfile = NULL, *chCAstore = NULL, *host = NULL;
     char *port = OPENSSL_strdup(PORT);
     char *bindhost = NULL, *bindport = NULL;
-    char *passarg = NULL, *pass = NULL, *vfyCApath = NULL, *vfyCAfile = NULL;
+    char *passarg = NULL, *pass = NULL;
+    char *vfyCApath = NULL, *vfyCAfile = NULL, *vfyCAstore = NULL;
     char *ReqCAfile = NULL;
     char *sess_in = NULL, *crl_file = NULL, *p;
     const char *protohost = NULL;
     struct timeval timeout, *timeoutp;
     fd_set readfds, writefds;
-    int noCApath = 0, noCAfile = 0;
+    int noCApath = 0, noCAfile = 0, noCAstore = 0;
     int build_chain = 0, cbuf_len, cbuf_off, cert_format = FORMAT_PEM;
     int key_format = FORMAT_PEM, crlf = 0, full_log = 1, mbuf_len = 0;
     int prexit = 0;
@@ -1416,6 +1426,18 @@ int s_client_main(int argc, char **argv)
         case OPT_VERIFYCAFILE:
             vfyCAfile = opt_arg();
             break;
+        case OPT_CASTORE:
+            CAstore = opt_arg();
+            break;
+        case OPT_NOCASTORE:
+            noCAstore = 1;
+            break;
+        case OPT_CHAINCASTORE:
+            chCAstore = opt_arg();
+            break;
+        case OPT_VERIFYCASTORE:
+            vfyCAstore = opt_arg();
+            break;
         case OPT_DANE_TLSA_DOMAIN:
             dane_tlsa_domain = opt_arg();
             break;
@@ -1796,7 +1818,9 @@ int s_client_main(int argc, char **argv)
         goto end;
     }
 
-    if (!ssl_load_stores(ctx, vfyCApath, vfyCAfile, chCApath, chCAfile,
+    if (!ssl_load_stores(ctx,
+                         vfyCApath, vfyCAfile, vfyCAstore,
+                         chCApath, chCAfile, chCAstore,
                          crls, crl_download)) {
         BIO_printf(bio_err, "Error loading store locations\n");
         ERR_print_errors(bio_err);
@@ -1925,7 +1949,8 @@ int s_client_main(int argc, char **argv)
 
     SSL_CTX_set_verify(ctx, verify, verify_callback);
 
-    if (!ctx_set_verify_locations(ctx, CAfile, CApath, noCAfile, noCApath)) {
+    if (!ctx_set_verify_locations(ctx, CAfile, noCAfile, CApath, noCApath,
+                                  CAstore, noCAstore)) {
         ERR_print_errors(bio_err);
         goto end;
     }
diff --git a/apps/s_server.c b/apps/s_server.c
index 5f58ef68fe..03ff1b410e 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -735,7 +735,9 @@ typedef enum OPTION_choice {
     OPT_CAPATH, OPT_NOCAPATH, OPT_CHAINCAPATH, OPT_VERIFYCAPATH, OPT_NO_CACHE,
     OPT_EXT_CACHE, OPT_CRLFORM, OPT_VERIFY_RET_ERROR, OPT_VERIFY_QUIET,
     OPT_BUILD_CHAIN, OPT_CAFILE, OPT_NOCAFILE, OPT_CHAINCAFILE,
-    OPT_VERIFYCAFILE, OPT_NBIO, OPT_NBIO_TEST, OPT_IGN_EOF, OPT_NO_IGN_EOF,
+    OPT_VERIFYCAFILE,
+    OPT_CASTORE, OPT_NOCASTORE, OPT_CHAINCASTORE, OPT_VERIFYCASTORE,
+    OPT_NBIO, OPT_NBIO_TEST, OPT_IGN_EOF, OPT_NO_IGN_EOF,
     OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_STATUS_VERBOSE,
     OPT_STATUS_TIMEOUT, OPT_STATUS_URL, OPT_STATUS_FILE, OPT_MSG, OPT_MSGFILE,
     OPT_TRACE, OPT_SECURITY_DEBUG, OPT_SECURITY_DEBUG_VERBOSE, OPT_STATE,
@@ -807,10 +809,13 @@ const OPTIONS s_server_options[] = {
     {"state", OPT_STATE, '-', "Print the SSL states"},
     {"CAfile", OPT_CAFILE, '<', "PEM format file of CA's"},
     {"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"},
+    {"CAstore", OPT_CASTORE, ':', "URI to store of CA's"},
     {"no-CAfile", OPT_NOCAFILE, '-',
      "Do not load the default certificates file"},
     {"no-CApath", OPT_NOCAPATH, '-',
      "Do not load certificates from the default certificates directory"},
+    {"no-CAstore", OPT_NOCASTORE, '-',
+     "Do not load certificates from the default certificates store URI"},
     {"nocert", OPT_NOCERT, '-', "Don't use any certificates (Anon-DH)"},
     {"quiet", OPT_QUIET, '-', "No server output"},
     {"no_resume_ephemeral", OPT_NO_RESUME_EPHEMERAL, '-',
@@ -844,8 +849,12 @@ const OPTIONS s_server_options[] = {
      "second certificate chain file in PEM format"},
     {"chainCApath", OPT_CHAINCAPATH, '/',
      "use dir as certificate store path to build CA certificate chain"},
+    {"chainCAstore", OPT_CHAINCASTORE, ':',
+     "use URI as certificate store to build CA certificate chain"},
     {"verifyCApath", OPT_VERIFYCAPATH, '/',
      "use dir as certificate store path to verify CA certificate"},
+    {"verifyCAstore", OPT_VERIFYCASTORE, ':',
+     "use URI as certificate store to verify CA certificate"},
     {"no_cache", OPT_NO_CACHE, '-', "Disable session cache"},
     {"ext_cache", OPT_EXT_CACHE, '-',
      "Disable internal cache, setup and use external cache"},
@@ -986,9 +995,11 @@ int s_server_main(int argc, char *argv[])
     STACK_OF(X509_CRL) *crls = NULL;
     X509 *s_cert = NULL, *s_dcert = NULL;
     X509_VERIFY_PARAM *vpm = NULL;
-    const char *CApath = NULL, *CAfile = NULL, *chCApath = NULL, *chCAfile = NULL;
+    const char *CApath = NULL, *CAfile = NULL, *CAstore = NULL;
+    const char *chCApath = NULL, *chCAfile = NULL, *chCAstore = NULL;
     char *dpassarg = NULL, *dpass = NULL;
-    char *passarg = NULL, *pass = NULL, *vfyCApath = NULL, *vfyCAfile = NULL;
+    char *passarg = NULL, *pass = NULL;
+    char *vfyCApath = NULL, *vfyCAfile = NULL, *vfyCAstore = NULL;
     char *crl_file = NULL, *prog;
 #ifdef AF_UNIX
     int unlink_unix_path = 0;
@@ -1000,7 +1011,7 @@ int s_server_main(int argc, char *argv[])
     int no_dhe = 0;
 #endif
     int nocert = 0, ret = 1;
-    int noCApath = 0, noCAfile = 0;
+    int noCApath = 0, noCAfile = 0, noCAstore = 0;
     int s_cert_format = FORMAT_PEM, s_key_format = FORMAT_PEM;
     int s_dcert_format = FORMAT_PEM, s_dkey_format = FORMAT_PEM;
     int rev = 0, naccept = -1, sdebug = 0;
@@ -1258,6 +1269,18 @@ int s_server_main(int argc, char *argv[])
         case OPT_VERIFYCAPATH:
             vfyCApath = opt_arg();
             break;
+        case OPT_CASTORE:
+            CAstore = opt_arg();
+            break;
+        case OPT_NOCASTORE:
+            noCAstore = 1;
+            break;
+        case OPT_CHAINCASTORE:
+            chCAstore = opt_arg();
+            break;
+        case OPT_VERIFYCASTORE:
+            vfyCAstore = opt_arg();
+            break;
         case OPT_NO_CACHE:
             no_cache = 1;
             break;
@@ -1880,7 +1903,8 @@ int s_server_main(int argc, char *argv[])
     }
 #endif
 
-    if (!ctx_set_verify_locations(ctx, CAfile, CApath, noCAfile, noCApath)) {
+    if (!ctx_set_verify_locations(ctx, CAfile, noCAfile, CApath, noCApath,
+                                  CAstore, noCAstore)) {
         ERR_print_errors(bio_err);
         goto end;
     }
@@ -1892,7 +1916,9 @@ int s_server_main(int argc, char *argv[])
 
     ssl_ctx_add_crls(ctx, crls, 0);
 
-    if (!ssl_load_stores(ctx, vfyCApath, vfyCAfile, chCApath, chCAfile,
+    if (!ssl_load_stores(ctx,
+                         vfyCApath, vfyCAfile, vfyCAstore,
+                         chCApath, chCAfile, chCAstore,
                          crls, crl_download)) {
         BIO_printf(bio_err, "Error loading store locations\n");
         ERR_print_errors(bio_err);
@@ -1941,8 +1967,8 @@ int s_server_main(int argc, char *argv[])
         if (async)
             SSL_CTX_set_mode(ctx2, SSL_MODE_ASYNC);
 
-        if (!ctx_set_verify_locations(ctx2, CAfile, CApath, noCAfile,
-                                      noCApath)) {
+        if (!ctx_set_verify_locations(ctx2, CAfile, noCAfile, CApath,
+                                      noCApath, CAstore, noCAstore)) {
             ERR_print_errors(bio_err);
             goto end;
         }
diff --git a/apps/s_time.c b/apps/s_time.c
index f6dbfa0462..43bec59037 100644
--- a/apps/s_time.c
+++ b/apps/s_time.c
@@ -45,8 +45,9 @@ static const size_t fmt_http_get_cmd_size = sizeof(fmt_http_get_cmd) - 2;
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_CONNECT, OPT_CIPHER, OPT_CIPHERSUITES, OPT_CERT, OPT_NAMEOPT, OPT_KEY,
-    OPT_CAPATH, OPT_CAFILE, OPT_NOCAPATH, OPT_NOCAFILE, OPT_NEW, OPT_REUSE,
-    OPT_BUGS, OPT_VERIFY, OPT_TIME, OPT_SSL3,
+    OPT_CAPATH, OPT_CAFILE, OPT_CASTORE,
+    OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE,
+    OPT_NEW, OPT_REUSE, OPT_BUGS, OPT_VERIFY, OPT_TIME, OPT_SSL3,
     OPT_WWW, OPT_TLS1, OPT_TLS1_1, OPT_TLS1_2, OPT_TLS1_3
 } OPTION_CHOICE;
 
@@ -60,12 +61,15 @@ const OPTIONS s_time_options[] = {
     {"cert", OPT_CERT, '<', "Cert file to use, PEM format assumed"},
     {"nameopt", OPT_NAMEOPT, 's', "Various certificate name options"},
     {"key", OPT_KEY, '<', "File with key, PEM; default is -cert file"},
-    {"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"},
     {"cafile", OPT_CAFILE, '<', "PEM format file of CA's"},
+    {"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"},
+    {"CAstore", OPT_CASTORE, ':', "URI to store of CA's"},
     {"no-CAfile", OPT_NOCAFILE, '-',
      "Do not load the default certificates file"},
     {"no-CApath", OPT_NOCAPATH, '-',
      "Do not load certificates from the default certificates directory"},
+    {"no-CAstore", OPT_NOCASTORE, '-',
+     "Do not load certificates from the default certificates store URI"},
     {"new", OPT_NEW, '-', "Just time new connections"},
     {"reuse", OPT_REUSE, '-', "Just time connection reuse"},
     {"bugs", OPT_BUGS, '-', "Turn on SSL bug compatibility"},
@@ -105,11 +109,12 @@ int s_time_main(int argc, char **argv)
     SSL *scon = NULL;
     SSL_CTX *ctx = NULL;
     const SSL_METHOD *meth = NULL;
-    char *CApath = NULL, *CAfile = NULL, *cipher = NULL, *ciphersuites = NULL;
+    char *CApath = NULL, *CAfile = NULL, *CAstore = NULL;
+    char *cipher = NULL, *ciphersuites = NULL;
     char *www_path = NULL;
     char *host = SSL_CONNECT_NAME, *certfile = NULL, *keyfile = NULL, *prog;
     double totalTime = 0.0;
-    int noCApath = 0, noCAfile = 0;
+    int noCApath = 0, noCAfile = 0, noCAstore = 0;
     int maxtime = SECONDS, nConn = 0, perform = 3, ret = 1, i, st_bugs = 0;
     long bytes_read = 0, finishtime = 0;
     OPTION_CHOICE o;
@@ -167,6 +172,12 @@ int s_time_main(int argc, char **argv)
         case OPT_NOCAFILE:
             noCAfile = 1;
             break;
+        case OPT_CASTORE:
+            CAstore = opt_arg();
+            break;
+        case OPT_NOCASTORE:
+            noCAstore = 1;
+            break;
         case OPT_CIPHER:
             cipher = opt_arg();
             break;
@@ -236,7 +247,8 @@ int s_time_main(int argc, char **argv)
     if (!set_cert_stuff(ctx, certfile, keyfile))
         goto end;
 
-    if (!ctx_set_verify_locations(ctx, CAfile, CApath, noCAfile, noCApath)) {
+    if (!ctx_set_verify_locations(ctx, CAfile, noCAfile, CApath, noCApath,
+                                  CAstore, noCAstore)) {
         ERR_print_errors(bio_err);
         goto end;
     }
diff --git a/apps/smime.c b/apps/smime.c
index 0f99e0a5e9..26bd028bea 100644
--- a/apps/smime.c
+++ b/apps/smime.c
@@ -41,9 +41,10 @@ typedef enum OPTION_choice {
     OPT_CRLFEOL, OPT_ENGINE, OPT_PASSIN,
     OPT_TO, OPT_FROM, OPT_SUBJECT, OPT_SIGNER, OPT_RECIP, OPT_MD,
     OPT_CIPHER, OPT_INKEY, OPT_KEYFORM, OPT_CERTFILE, OPT_CAFILE,
+    OPT_CAPATH, OPT_CASTORE, OPT_NOCAFILE, OPT_NOCAPATH, OPT_NOCASTORE,
     OPT_R_ENUM,
     OPT_V_ENUM,
-    OPT_CAPATH, OPT_NOCAFILE, OPT_NOCAPATH, OPT_IN, OPT_INFORM, OPT_OUT,
+    OPT_IN, OPT_INFORM, OPT_OUT,
     OPT_OUTFORM, OPT_CONTENT
 } OPTION_CHOICE;
 
@@ -86,10 +87,13 @@ const OPTIONS smime_options[] = {
     {"text", OPT_TEXT, '-', "Include or delete text MIME headers"},
     {"CApath", OPT_CAPATH, '/', "Trusted certificates directory"},
     {"CAfile", OPT_CAFILE, '<', "Trusted certificates file"},
+    {"CAstore", OPT_CASTORE, ':', "Trusted certificates store URI"},
     {"no-CAfile", OPT_NOCAFILE, '-',
      "Do not load the default certificates file"},
     {"no-CApath", OPT_NOCAPATH, '-',
      "Do not load certificates from the default certificates directory"},
+    {"no-CAstore", OPT_NOCASTORE, '-',
+     "Do not load certificates from the default certificates store"},
     {"resign", OPT_RESIGN, '-', "Resign a signed message"},
     {"nochain", OPT_NOCHAIN, '-',
      "set PKCS7_NOCHAIN so certificates contained in the message are not used as untrusted CAs" },
@@ -121,12 +125,12 @@ int smime_main(int argc, char **argv)
     X509_VERIFY_PARAM *vpm = NULL;
     const EVP_CIPHER *cipher = NULL;
     const EVP_MD *sign_md = NULL;
-    const char *CAfile = NULL, *CApath = NULL, *prog = NULL;
+    const char *CAfile = NULL, *CApath = NULL, *CAstore = NULL, *prog = NULL;
     char *certfile = NULL, *keyfile = NULL, *contfile = NULL;
     char *infile = NULL, *outfile = NULL, *signerfile = NULL, *recipfile = NULL;
     char *passinarg = NULL, *passin = NULL, *to = NULL, *from = NULL, *subject = NULL;
     OPTION_CHOICE o;
-    int noCApath = 0, noCAfile = 0;
+    int noCApath = 0, noCAfile = 0, noCAstore = 0;
     int flags = PKCS7_DETACHED, operation = 0, ret = 0, indef = 0;
     int informat = FORMAT_SMIME, outformat = FORMAT_SMIME, keyform =
         FORMAT_PEM;
@@ -302,12 +306,18 @@ int smime_main(int argc, char **argv)
         case OPT_CAPATH:
             CApath = opt_arg();
             break;
+        case OPT_CASTORE:
+            CAstore = opt_arg();
+            break;
         case OPT_NOCAFILE:
             noCAfile = 1;
             break;
         case OPT_NOCAPATH:
             noCApath = 1;
             break;
+        case OPT_NOCASTORE:
+            noCAstore = 1;
+            break;
         case OPT_CONTENT:
             contfile = opt_arg();
             break;
@@ -473,7 +483,8 @@ int smime_main(int argc, char **argv)
         goto end;
 
     if (operation == SMIME_VERIFY) {
-        if ((store = setup_verify(CAfile, CApath, noCAfile, noCApath)) == NULL)
+        if ((store = setup_verify(CAfile, noCAfile, CApath, noCApath,
+                                  CAstore, noCAstore)) == NULL)
             goto end;
         X509_STORE_set_verify_cb(store, smime_cb);
         if (vpmtouched)
diff --git a/apps/ts.c b/apps/ts.c
index b45c262789..9fdba649c1 100644
--- a/apps/ts.c
+++ b/apps/ts.c
@@ -66,15 +66,17 @@ static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial);
 /* Verify related functions. */
 static int verify_command(const char *data, const char *digest, const char *queryfile,
                           const char *in, int token_in,
-                          const char *CApath, const char *CAfile, const char *untrusted,
-                          X509_VERIFY_PARAM *vpm);
+                          const char *CApath, const char *CAfile,
+                          const char *CAstore,
+                          const char *untrusted, X509_VERIFY_PARAM *vpm);
 static TS_VERIFY_CTX *create_verify_ctx(const char *data, const char *digest,
                                         const char *queryfile,
                                         const char *CApath, const char *CAfile,
+                                        const char *CAstore,
                                         const char *untrusted,
                                         X509_VERIFY_PARAM *vpm);
 static X509_STORE *create_cert_store(const char *CApath, const char *CAfile,
-                                     X509_VERIFY_PARAM *vpm);
+                                     const char *CAstore, X509_VERIFY_PARAM *vpm);
 static int verify_cb(int ok, X509_STORE_CTX *ctx);
 
 typedef enum OPTION_choice {
@@ -83,7 +85,7 @@ typedef enum OPTION_choice {
     OPT_DIGEST, OPT_TSPOLICY, OPT_NO_NONCE, OPT_CERT,
     OPT_IN, OPT_TOKEN_IN, OPT_OUT, OPT_TOKEN_OUT, OPT_TEXT,
     OPT_REPLY, OPT_QUERYFILE, OPT_PASSIN, OPT_INKEY, OPT_SIGNER,
-    OPT_CHAIN, OPT_VERIFY, OPT_CAPATH, OPT_CAFILE, OPT_UNTRUSTED,
+    OPT_CHAIN, OPT_VERIFY, OPT_CAPATH, OPT_CAFILE, OPT_CASTORE, OPT_UNTRUSTED,
     OPT_MD, OPT_V_ENUM, OPT_R_ENUM
 } OPTION_CHOICE;
 
@@ -112,6 +114,7 @@ const OPTIONS ts_options[] = {
     {"verify", OPT_VERIFY, '-', "Verify a TS response"},
     {"CApath", OPT_CAPATH, '/', "Path to trusted CA files"},
     {"CAfile", OPT_CAFILE, '<', "File with trusted CA certs"},
+    {"CAstore", OPT_CASTORE, ':', "URI to trusted CA store"},
     {"untrusted", OPT_UNTRUSTED, '<', "File with untrusted certs"},
     {"", OPT_MD, '-', "Any supported digest"},
 # ifndef OPENSSL_NO_ENGINE
@@ -143,7 +146,7 @@ static char* opt_helplist[] = {
     "          [-text]",
 # endif
     "  or",
-    "ts -verify -CApath dir -CAfile file.pem -untrusted file.pem",
+    "ts -verify -CApath dir -CAfile file.pem -CAstore uri -untrusted file.pem",
     "           [-data file] [-digest hexstring]",
     "           [-queryfile file] -in file [-token_in]",
     "           [[options specific to 'ts -verify']]",
@@ -161,6 +164,7 @@ int ts_main(int argc, char **argv)
     char *data = NULL, *digest = NULL, *policy = NULL;
     char *in = NULL, *out = NULL, *queryfile = NULL, *passin = NULL;
     char *inkey = NULL, *signer = NULL, *chain = NULL, *CApath = NULL;
+    char *CAstore = NULL;
     const EVP_MD *md = NULL;
     OPTION_CHOICE o, mode = OPT_ERR;
     int ret = 1, no_nonce = 0, cert = 0, text = 0;
@@ -256,6 +260,9 @@ int ts_main(int argc, char **argv)
         case OPT_CAFILE:
             CAfile = opt_arg();
             break;
+        case OPT_CASTORE:
+            CAstore = opt_arg();
+            break;
         case OPT_UNTRUSTED:
             untrusted = opt_arg();
             break;
@@ -311,7 +318,7 @@ int ts_main(int argc, char **argv)
         if ((in == NULL) || !EXACTLY_ONE(queryfile, data, digest))
             goto opthelp;
         ret = !verify_command(data, digest, queryfile, in, token_in,
-                              CApath, CAfile, untrusted,
+                              CApath, CAfile, CAstore, untrusted,
                               vpmtouched ? vpm : NULL);
     } else {
         goto opthelp;
@@ -820,7 +827,8 @@ static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial)
 
 static int verify_command(const char *data, const char *digest, const char *queryfile,
                           const char *in, int token_in,
-                          const char *CApath, const char *CAfile, const char *untrusted,
+                          const char *CApath, const char *CAfile,
+                          const char *CAstore, const char *untrusted,
                           X509_VERIFY_PARAM *vpm)
 {
     BIO *in_bio = NULL;
@@ -840,7 +848,7 @@ static int verify_command(const char *data, const char *digest, const char *quer
     }
 
     if ((verify_ctx = create_verify_ctx(data, digest, queryfile,
-                                        CApath, CAfile, untrusted,
+                                        CApath, CAfile, CAstore, untrusted,
                                         vpm)) == NULL)
         goto end;
 
@@ -867,6 +875,7 @@ static int verify_command(const char *data, const char *digest, const char *quer
 static TS_VERIFY_CTX *create_verify_ctx(const char *data, const char *digest,
                                         const char *queryfile,
                                         const char *CApath, const char *CAfile,
+                                        const char *CAstore,
                                         const char *untrusted,
                                         X509_VERIFY_PARAM *vpm)
 {
@@ -915,7 +924,8 @@ static TS_VERIFY_CTX *create_verify_ctx(const char *data, const char *digest,
     TS_VERIFY_CTX_add_flags(ctx, f | TS_VFY_SIGNATURE);
 
     /* Initialising the X509_STORE object. */
-    if (TS_VERIFY_CTX_set_store(ctx, create_cert_store(CApath, CAfile, vpm))
+    if (TS_VERIFY_CTX_set_store(ctx,
+                                create_cert_store(CApath, CAfile, CAstore, vpm))
             == NULL)
         goto err;
 
@@ -936,11 +946,10 @@ static TS_VERIFY_CTX *create_verify_ctx(const char *data, const char *digest,
 }
 
 static X509_STORE *create_cert_store(const char *CApath, const char *CAfile,
-                                     X509_VERIFY_PARAM *vpm)
+                                     const char *CAstore, X509_VERIFY_PARAM *vpm)
 {
     X509_STORE *cert_ctx = NULL;
     X509_LOOKUP *lookup = NULL;
-    int i;
 
     cert_ctx = X509_STORE_new();
     X509_STORE_set_verify_cb(cert_ctx, verify_cb);
@@ -950,8 +959,7 @@ static X509_STORE *create_cert_store(const char *CApath, const char *CAfile,
             BIO_printf(bio_err, "memory allocation failure\n");
             goto err;
         }
-        i = X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM);
-        if (!i) {
+        if (!X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM)) {
             BIO_printf(bio_err, "Error loading directory %s\n", CApath);
             goto err;
         }
@@ -963,13 +971,24 @@ static X509_STORE *create_cert_store(const char *CApath, const char *CAfile,
             BIO_printf(bio_err, "memory allocation failure\n");
             goto err;
         }
-        i = X509_LOOKUP_load_file(lookup, CAfile, X509_FILETYPE_PEM);
-        if (!i) {
+        if (!X509_LOOKUP_load_file(lookup, CAfile, X509_FILETYPE_PEM)) {
             BIO_printf(bio_err, "Error loading file %s\n", CAfile);
             goto err;
         }
     }
 
+    if (CAstore != NULL) {
+        lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_store());
+        if (lookup == NULL) {
+            BIO_printf(bio_err, "memory allocation failure\n");
+            goto err;
+        }
+        if (!X509_LOOKUP_load_store(lookup, CAstore)) {
+            BIO_printf(bio_err, "Error loading store URI %s\n", CAstore);
+            goto err;
+        }
+    }
+
     if (vpm != NULL)
         X509_STORE_set1_param(cert_ctx, vpm);
 
diff --git a/apps/verify.c b/apps/verify.c
index fd407646f5..7b18de2bab 100644
--- a/apps/verify.c
+++ b/apps/verify.c
@@ -27,7 +27,8 @@ static int v_verbose = 0, vflags = 0;
 
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
-    OPT_ENGINE, OPT_CAPATH, OPT_CAFILE, OPT_NOCAPATH, OPT_NOCAFILE,
+    OPT_ENGINE, OPT_CAPATH, OPT_CAFILE, OPT_CASTORE,
+    OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE,
     OPT_UNTRUSTED, OPT_TRUSTED, OPT_CRLFILE, OPT_CRL_DOWNLOAD, OPT_SHOW_CHAIN,
     OPT_V_ENUM, OPT_NAMEOPT,
     OPT_VERBOSE, OPT_SM2ID, OPT_SM2HEXID
@@ -41,10 +42,13 @@ const OPTIONS verify_options[] = {
         "Print extra information about the operations being performed."},
     {"CApath", OPT_CAPATH, '/', "A directory of trusted certificates"},
     {"CAfile", OPT_CAFILE, '<', "A file of trusted certificates"},
+    {"CAstore", OPT_CASTORE, ':', "URI to a store of trusted certificates"},
     {"no-CAfile", OPT_NOCAFILE, '-',
      "Do not load the default certificates file"},
     {"no-CApath", OPT_NOCAPATH, '-',
      "Do not load certificates from the default certificates directory"},
+    {"no-CAstore", OPT_NOCAPATH, '-',
+     "Do not load certificates from the default certificates store"},
     {"untrusted", OPT_UNTRUSTED, '<', "A file of untrusted certificates"},
     {"trusted", OPT_TRUSTED, '<', "A file of trusted certificates"},
     {"CRLfile", OPT_CRLFILE, '<',
@@ -74,8 +78,8 @@ int verify_main(int argc, char **argv)
     STACK_OF(X509_CRL) *crls = NULL;
     X509_STORE *store = NULL;
     X509_VERIFY_PARAM *vpm = NULL;
-    const char *prog, *CApath = NULL, *CAfile = NULL;
-    int noCApath = 0, noCAfile = 0;
+    const char *prog, *CApath = NULL, *CAfile = NULL, *CAstore = NULL;
+    int noCApath = 0, noCAfile = 0, noCAstore = 0;
     int vpmtouched = 0, crl_download = 0, show_chain = 0, i = 0, ret = 1;
     OPTION_CHOICE o;
     unsigned char *sm2_id = NULL;
@@ -123,12 +127,18 @@ int verify_main(int argc, char **argv)
         case OPT_CAFILE:
             CAfile = opt_arg();
             break;
+        case OPT_CASTORE:
+            CAstore = opt_arg();
+            break;
         case OPT_NOCAPATH:
             noCApath = 1;
             break;
         case OPT_NOCAFILE:
             noCAfile = 1;
             break;
+        case OPT_NOCASTORE:
+            noCAstore = 1;
+            break;
         case OPT_UNTRUSTED:
             /* Zero or more times */
             if (!load_certs(opt_arg(), &untrusted, FORMAT_PEM, NULL,
@@ -139,6 +149,7 @@ int verify_main(int argc, char **argv)
             /* Zero or more times */
             noCAfile = 1;
             noCApath = 1;
+            noCAstore = 1;
             if (!load_certs(opt_arg(), &trusted, FORMAT_PEM, NULL,
                             "trusted certificates"))
                 goto end;
@@ -195,14 +206,16 @@ int verify_main(int argc, char **argv)
     }
     argc = opt_num_rest();
     argv = opt_rest();
-    if (trusted != NULL && (CAfile || CApath)) {
+    if (trusted != NULL
+        && (CAfile != NULL || CApath != NULL || CAstore != NULL)) {
         BIO_printf(bio_err,
-                   "%s: Cannot use -trusted with -CAfile or -CApath\n",
+                   "%s: Cannot use -trusted with -CAfile, -CApath or -CAstore\n",
                    prog);
         goto end;
     }
 
-    if ((store = setup_verify(CAfile, CApath, noCAfile, noCApath)) == NULL)
+    if ((store = setup_verify(CAfile, noCAfile, CApath, noCApath,
+                              CAstore, noCAstore)) == NULL)
         goto end;
     X509_STORE_set_verify_cb(store, cb);
 
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 27aad6a6ce..abbf232133 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -1828,6 +1828,7 @@ X509_F_COMMON_VERIFY_SM2:165:common_verify_sm2
 X509_F_DANE_I2D:107:dane_i2d
 X509_F_DIR_CTRL:102:dir_ctrl
 X509_F_GET_CERT_BY_SUBJECT:103:get_cert_by_subject
+X509_F_CACHE_OBJECTS:163:cache_objects
 X509_F_I2D_X509_AUX:151:i2d_X509_AUX
 X509_F_LOOKUP_CERTS_SK:152:lookup_certs_sk
 X509_F_NETSCAPE_SPKI_B64_DECODE:129:NETSCAPE_SPKI_b64_decode
diff --git a/crypto/store/loader_file.c b/crypto/store/loader_file.c
index 078c7c2573..cf9951f0fd 100644
--- a/crypto/store/loader_file.c
+++ b/crypto/store/loader_file.c
@@ -930,7 +930,8 @@ static int file_expect(OSSL_STORE_LOADER_CTX *ctx, int expected)
     return 1;
 }
 
-static int file_find(OSSL_STORE_LOADER_CTX *ctx, OSSL_STORE_SEARCH *search)
+static int file_find(OSSL_STORE_LOADER_CTX *ctx,
+                     const OSSL_STORE_SEARCH *search)
 {
     /*
      * If ctx == NULL, the library is looking to know if this loader supports
diff --git a/crypto/store/store_lib.c b/crypto/store/store_lib.c
index 92b957c7cd..d39967ccc4 100644
--- a/crypto/store/store_lib.c
+++ b/crypto/store/store_lib.c
@@ -16,6 +16,7 @@
 
 #include <openssl/crypto.h>
 #include <openssl/err.h>
+#include <openssl/trace.h>
 #include <openssl/store.h>
 #include "internal/thread_once.h"
 #include "crypto/store.h"
@@ -74,9 +75,14 @@ OSSL_STORE_CTX *OSSL_STORE_open(const char *uri, const UI_METHOD *ui_method,
 
     /* Try each scheme until we find one that could open the URI */
     for (i = 0; loader_ctx == NULL && i < schemes_n; i++) {
-        if ((loader = ossl_store_get0_loader_int(schemes[i])) != NULL)
+        OSSL_TRACE1(STORE, "Looking up scheme %s\n", schemes[i]);
+        if ((loader = ossl_store_get0_loader_int(schemes[i])) != NULL) {
+            OSSL_TRACE1(STORE, "Found loader for scheme %s\n", schemes[i]);
             loader_ctx = loader->open(loader, uri, ui_method, ui_data);
+            OSSL_TRACE2(STORE, "Opened %s => %p\n", uri, (void *)loader_ctx);
+        }
     }
+
     if (loader_ctx == NULL)
         goto err;
 
@@ -147,7 +153,7 @@ int OSSL_STORE_expect(OSSL_STORE_CTX *ctx, int expected_type)
     return 1;
 }
 
-int OSSL_STORE_find(OSSL_STORE_CTX *ctx, OSSL_STORE_SEARCH *search)
+int OSSL_STORE_find(OSSL_STORE_CTX *ctx, const OSSL_STORE_SEARCH *search)
 {
     if (ctx->loading) {
         OSSL_STOREerr(OSSL_STORE_F_OSSL_STORE_FIND,
@@ -172,6 +178,7 @@ OSSL_STORE_INFO *OSSL_STORE_load(OSSL_STORE_CTX *ctx)
     if (OSSL_STORE_eof(ctx))
         return NULL;
 
+    OSSL_TRACE(STORE, "Loading next object\n");
     v = ctx->loader->load(ctx->loader_ctx, ctx->ui_method, ctx->ui_data);
 
     if (ctx->post_process != NULL && v != NULL) {
@@ -203,6 +210,10 @@ OSSL_STORE_INFO *OSSL_STORE_load(OSSL_STORE_CTX *ctx)
         }
     }
 
+    if (v != NULL)
+        OSSL_TRACE1(STORE, "Got a %s\n",
+                    OSSL_STORE_INFO_type_string(OSSL_STORE_INFO_get_type(v)));
+
     return v;
 }
 
@@ -218,7 +229,10 @@ int OSSL_STORE_eof(OSSL_STORE_CTX *ctx)
 
 int OSSL_STORE_close(OSSL_STORE_CTX *ctx)
 {
-    int loader_ret = ctx->loader->close(ctx->loader_ctx);
+    int loader_ret;
+
+    OSSL_TRACE1(STORE, "Closing %p\n", (void *)ctx->loader_ctx);
+    loader_ret = ctx->loader->close(ctx->loader_ctx);
 
     OPENSSL_free(ctx);
     return loader_ret;
@@ -569,7 +583,7 @@ int OSSL_STORE_SEARCH_get_type(const OSSL_STORE_SEARCH *criterion)
     return criterion->search_type;
 }
 
-X509_NAME *OSSL_STORE_SEARCH_get0_name(OSSL_STORE_SEARCH *criterion)
+X509_NAME *OSSL_STORE_SEARCH_get0_name(const OSSL_STORE_SEARCH *criterion)
 {
     return criterion->name;
 }
diff --git a/crypto/trace.c b/crypto/trace.c
index 7ff6af8837..18a8c64135 100755
--- a/crypto/trace.c
+++ b/crypto/trace.c
@@ -133,6 +133,7 @@ static const struct trace_category_st trace_categories[] = {
     TRACE_CATEGORY_(PKCS12_DECRYPT),
     TRACE_CATEGORY_(X509V3_POLICY),
     TRACE_CATEGORY_(BN_CTX),
+    TRACE_CATEGORY_(STORE),
 };
 
 const char *OSSL_trace_get_category_name(int num)
diff --git a/crypto/x509/build.info b/crypto/x509/build.info
index bee9f80961..ca7bb2a03f 100644
--- a/crypto/x509/build.info
+++ b/crypto/x509/build.info
@@ -5,7 +5,7 @@ SOURCE[../../libcrypto]=\
         x509_set.c x509cset.c x509rset.c x509_err.c \
         x509name.c x509_v3.c x509_ext.c x509_att.c \
         x509type.c x509_meth.c x509_lu.c x_all.c x509_txt.c \
-        x509_trs.c by_file.c by_dir.c x509_vpm.c \
+        x509_trs.c by_file.c by_dir.c by_store.c x509_vpm.c \
         x_crl.c t_crl.c x_req.c t_req.c x_x509.c t_x509.c \
         x_pubkey.c x_x509a.c x_attrib.c x_exten.c x_name.c \
         v3_bcons.c v3_bitst.c v3_conf.c v3_extku.c v3_ia5.c v3_lib.c \
diff --git a/crypto/x509/by_store.c b/crypto/x509/by_store.c
new file mode 100644
index 0000000000..b2264d7123
--- /dev/null
+++ b/crypto/x509/by_store.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/store.h>
+#include "internal/cryptlib.h"
+#include "crypto/x509.h"
+#include "x509_local.h"
+
+/* Generic object loader, given expected type and criterion */
+static int cache_objects(X509_LOOKUP *lctx, const char *uri,
+                         const OSSL_STORE_SEARCH *criterion,
+                         int depth)
+{
+    int ok = 0;
+    OSSL_STORE_CTX *ctx = NULL;
+    X509_STORE *xstore = X509_LOOKUP_get_store(lctx);
+
+    if ((ctx = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL)) == NULL)
+        return 0;
+
+    /*
+     * We try to set the criterion, but don't care if it was valid or not.
+     * For a OSSL_STORE, it merely serves as an optimization, the expectation
+     * being that if the criterion couldn't be used, we will get *everything*
+     * from the container that the URI represents rather than the subset that
+     * the criterion indicates, so the biggest harm is that we cache more
+     * objects certs and CRLs than we may expect, but that's ok.
+     *
+     * Specifically for OpenSSL's own file: scheme, the only workable
+     * criterion is the BY_NAME one, which it can only apply on directories,
+     * but it's possible that the URI is a single file rather than a directory,
+     * and in that case, the BY_NAME criterion is pointless.
+     *
+     * We could very simply not apply any criterion at all here, and just let
+     * the code that selects certs and CRLs from the cached objects do its job,
+     * but it's a nice optimization when it can be applied (such as on an
+     * actual directory with a thousand CA certs).
+     */
+    if (criterion != NULL)
+        OSSL_STORE_find(ctx, criterion);
+
+    for (;;) {
+        OSSL_STORE_INFO *info = OSSL_STORE_load(ctx);
+        int infotype;
+
+        /* NULL means error or "end of file".  Either way, we break. */
+        if (info == NULL)
+            break;
+
+        infotype = OSSL_STORE_INFO_get_type(info);
+        ok = 0;
+
+        if (infotype == OSSL_STORE_INFO_NAME) {
+            /*
+             * This is an entry in the "directory" represented by the current
+             * uri.  if |depth| allows, dive into it.
+             */
+            if (depth > 0)
+                ok = cache_objects(lctx, OSSL_STORE_INFO_get0_NAME(info),
+                                   criterion, depth - 1);
+        } else {
+            /*
+             * We know that X509_STORE_add_{cert|crl} increments the object's
+             * refcount, so we can safely use OSSL_STORE_INFO_get0_{cert,crl}
+             * to get them.
+             */
+            switch (infotype) {
+            case OSSL_STORE_INFO_CERT:
+                ok = X509_STORE_add_cert(xstore,
+                                         OSSL_STORE_INFO_get0_CERT(info));
+                break;
+            case OSSL_STORE_INFO_CRL:
+                ok = X509_STORE_add_crl(xstore,
+                                        OSSL_STORE_INFO_get0_CRL(info));
+                break;
+            }
+        }
+
+        OSSL_STORE_INFO_free(info);
+        if (!ok)
+            break;
+    }
+    OSSL_STORE_close(ctx);
+
+    return ok;
+}
+
+
+/* Because OPENSSL_free is a macro and for C type match */
+static void free_uri(OPENSSL_STRING data)
+{
+    OPENSSL_free(data);
+}
+
+static void by_store_free(X509_LOOKUP *ctx)
+{
+    STACK_OF(OPENSSL_STRING) *uris = X509_LOOKUP_get_method_data(ctx);
+    sk_OPENSSL_STRING_pop_free(uris, free_uri);
+}
+
+static int by_store_ctrl(X509_LOOKUP *ctx, int cmd,
+                         const char *argp, long argl,
+                         char **retp)
+{
+    switch (cmd) {
+    case X509_L_ADD_STORE:
+        /* If no URI is given, use the default cert dir as default URI */
+        if (argp == NULL)
+            argp = ossl_safe_getenv(X509_get_default_cert_dir_env());
+        if (argp == NULL)
+            argp = X509_get_default_cert_dir();
+
+        {
+            STACK_OF(OPENSSL_STRING) *uris = X509_LOOKUP_get_method_data(ctx);
+
+            if (uris == NULL) {
+                uris = sk_OPENSSL_STRING_new_null();
+                X509_LOOKUP_set_method_data(ctx, uris);
+            }
+            return sk_OPENSSL_STRING_push(uris, OPENSSL_strdup(argp)) > 0;
+        }
+    case X509_L_LOAD_STORE:
+        /* This is a shortcut for quick loading of specific containers */
+        return cache_objects(ctx, argp, NULL, 0);
+    }
+
+    return 0;
+}
+
+static int by_store(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type,
+                    const OSSL_STORE_SEARCH *criterion, X509_OBJECT *ret)
+{
+    STACK_OF(OPENSSL_STRING) *uris = X509_LOOKUP_get_method_data(ctx);
+    int i;
+    int ok = 0;
+
+    for (i = 0; i < sk_OPENSSL_STRING_num(uris); i++) {
+        ok = cache_objects(ctx, sk_OPENSSL_STRING_value(uris, i), criterion,
+                           1 /* depth */);
+
+        if (ok)
+            break;
+    }
+    return ok;
+}
+
+static int by_store_subject(X509_LOOKUP *ctx, X509_LOOKUP_TYPE type,
+                            X509_NAME *name, X509_OBJECT *ret)
+{
+    OSSL_STORE_SEARCH *criterion = OSSL_STORE_SEARCH_by_name(name);
+    int ok = by_store(ctx, type, criterion, ret);
+    STACK_OF(X509_OBJECT) *store_objects =
+        X509_STORE_get0_objects(X509_LOOKUP_get_store(ctx));
+    X509_OBJECT *tmp = NULL;
+
+    OSSL_STORE_SEARCH_free(criterion);
+
+    if (ok)
+        tmp = X509_OBJECT_retrieve_by_subject(store_objects, type, name);
+
+    ok = 0;
+    if (tmp != NULL) {
+        /*
+         * This could also be done like this:
+         *
+         *     if (tmp != NULL) {
+         *         *ret = *tmp;
+         *         ok = 1;
+         *     }
+         *
+         * However, we want to exercise the documented API to the max, so
+         * we do it the hard way.
+         *
+         * To be noted is that X509_OBJECT_set1_* increment the refcount,
+         * but so does X509_STORE_CTX_get_by_subject upon return of this
+         * function, so we must ensure the the refcount is decremented
+         * before we return, or we will get a refcount leak.  We cannot do
+         * this with X509_OBJECT_free(), though, as that will free a bit
+         * too much.
+         */
+        switch (type) {
+        case X509_LU_X509:
+            ok = X509_OBJECT_set1_X509(ret, tmp->data.x509);
+            if (ok)
+                X509_free(tmp->data.x509);
+            break;
+        case X509_LU_CRL:
+            ok = X509_OBJECT_set1_X509_CRL(ret, tmp->data.crl);
+            if (ok)
+                X509_CRL_free(tmp->data.crl);
+            break;
+        case X509_LU_NONE:
+            break;
+        }
+    }
+    return ok;
+}
+
+/*
+ * We lack the implementations for get_by_issuer_serial, get_by_fingerprint
+ * and get_by_alias.  There's simply not enough support in the X509_LOOKUP
+ * or X509_STORE APIs.
+ */
+
+static X509_LOOKUP_METHOD x509_store_lookup = {
+    "Load certs from STORE URIs",
+    NULL,                        /* new_item */
+    by_store_free,               /* free */
+    NULL,                        /* init */
+    NULL,                        /* shutdown */
+    by_store_ctrl,               /* ctrl */
+    by_store_subject,            /* get_by_subject */
+    NULL,                        /* get_by_issuer_serial */
+    NULL,                        /* get_by_fingerprint */
+    NULL,                        /* get_by_alias */
+};
+
+X509_LOOKUP_METHOD *X509_LOOKUP_store(void)
+{
+    return &x509_store_lookup;
+}
diff --git a/crypto/x509/x509_d2.c b/crypto/x509/x509_d2.c
index 70d57f5274..5beb7034a7 100644
--- a/crypto/x509/x509_d2.c
+++ b/crypto/x509/x509_d2.c
@@ -26,32 +26,64 @@ int X509_STORE_set_default_paths(X509_STORE *ctx)
         return 0;
     X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
 
+    lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_store());
+    if (lookup == NULL)
+        return 0;
+    X509_LOOKUP_add_store(lookup, NULL);
+
     /* clear any errors */
     ERR_clear_error();
 
     return 1;
 }
 
-int X509_STORE_load_locations(X509_STORE *ctx, const char *file,
-                              const char *path)
+int X509_STORE_load_file(X509_STORE *ctx, const char *file)
+{
+    X509_LOOKUP *lookup;
+
+    if (file == NULL
+        || (lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_file())) == NULL
+        || X509_LOOKUP_load_file(lookup, file, X509_FILETYPE_PEM) == 0)
+        return 0;
+
+    return 1;
+}
+
+int X509_STORE_load_path(X509_STORE *ctx, const char *path)
 {
     X509_LOOKUP *lookup;
 
-    if (file != NULL) {
-        lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_file());
-        if (lookup == NULL)
-            return 0;
-        if (X509_LOOKUP_load_file(lookup, file, X509_FILETYPE_PEM) != 1)
-            return 0;
-    }
-    if (path != NULL) {
-        lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_hash_dir());
-        if (lookup == NULL)
-            return 0;
-        if (X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) != 1)
-            return 0;
-    }
-    if ((path == NULL) && (file == NULL))
+    if (path == NULL
+        || (lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_hash_dir())) == NULL
+        || X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) == 0)
+        return 0;
+
+    return 1;
+}
+
+int X509_STORE_load_store(X509_STORE *ctx, const char *uri)
+{
+    X509_LOOKUP *lookup;
+
+    if (uri == NULL
+        || (lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_store())) == NULL
+        || X509_LOOKUP_add_store(lookup, uri) == 0)
+        return 0;
+
+    return 1;
+}
+
+/* Deprecated */
+#if OPENSSL_API_LEVEL < 3
+int X509_STORE_load_locations(X509_STORE *ctx, const char *file,
+                              const char *path)
+{
+    if (file == NULL && path == NULL)
+        return 0;
+    if (file != NULL && !X509_STORE_load_file(ctx, file))
+        return 0;
+    if (path != NULL && !X509_STORE_load_path(ctx, path))
         return 0;
     return 1;
 }
+#endif
diff --git a/doc/man1/openssl-cms.pod.in b/doc/man1/openssl-cms.pod.in
index 3d8bcd792e..83eb4fdb57 100644
--- a/doc/man1/openssl-cms.pod.in
+++ b/doc/man1/openssl-cms.pod.in
@@ -736,6 +736,10 @@ the list of permitted ciphers in a database and only use those.
 
 No revocation checking is done on the signer's certificate.
 
+=head1 SEE ALSO
+
+L<ossl_store-file(7)>
+
 =head1 HISTORY
 
 The use of multiple B<-signer> options and the B<-resign> command were first
diff --git a/doc/man1/openssl-crl.pod.in b/doc/man1/openssl-crl.pod.in
index 7441de446c..829c2a7235 100644
--- a/doc/man1/openssl-crl.pod.in
+++ b/doc/man1/openssl-crl.pod.in
@@ -120,7 +120,8 @@ and files too.
 L<openssl(1)>,
 L<openssl-crl2pkcs7(1)>,
 L<openssl-ca(1)>,
-L<openssl-x509(1)>
+L<openssl-x509(1)>,
+L<ossl_store-file(7)>
 
 =head1 COPYRIGHT
 
diff --git a/doc/man1/openssl-ocsp.pod.in b/doc/man1/openssl-ocsp.pod.in
index a3358e724a..e1634301a2 100644
--- a/doc/man1/openssl-ocsp.pod.in
+++ b/doc/man1/openssl-ocsp.pod.in
@@ -395,9 +395,9 @@ the OCSP request checked using the responder certificate's public key.
 
 Then a normal certificate verify is performed on the OCSP responder certificate
 building up a certificate chain in the process. The locations of the trusted
-certificates used to build the chain can be specified by the B<-CAfile>
-and B<-CApath> options or they will be looked for in the standard OpenSSL
-certificates directory.
+certificates used to build the chain can be specified by the B<-CAfile>,
+B<-CApath> or B<-CAstore> options or they will be looked for in the
+standard OpenSSL certificates directory.
 
 If the initial verify fails then the OCSP verify process halts with an
 error.
@@ -432,8 +432,8 @@ with the B<-VAfile> option.
 =head1 NOTES
 
 As noted, most of the verify options are for testing or debugging purposes.
-Normally only the B<-CApath>, B<-CAfile> and (if the responder is a 'global
-VA') B<-VAfile> options need to be used.
+Normally only the B<-CApath>, B<-CAfile>, B<-CAstore> and (if the responder
+is a 'global VA') B<-VAfile> options need to be used.
 
 The OCSP server is only useful for test and demonstration purposes: it is
 not really usable as a full OCSP responder. It contains only a very
diff --git a/doc/man1/openssl-pkcs12.pod.in b/doc/man1/openssl-pkcs12.pod.in
index 09b75111db..3eef9dc856 100644
--- a/doc/man1/openssl-pkcs12.pod.in
+++ b/doc/man1/openssl-pkcs12.pod.in
@@ -338,7 +338,8 @@ Include some extra certificates:
 =head1 SEE ALSO
 
 L<openssl(1)>,
-L<openssl-pkcs8(1)>
+L<openssl-pkcs8(1)>,
+L<ossl_store-file(7)>
 
 =head1 COPYRIGHT
 
diff --git a/doc/man1/openssl-s_client.pod.in b/doc/man1/openssl-s_client.pod.in
index 9752407a82..005e12ab2d 100644
--- a/doc/man1/openssl-s_client.pod.in
+++ b/doc/man1/openssl-s_client.pod.in
@@ -35,6 +35,7 @@ B<openssl> B<s_client>
 [B<-pass> I<arg>]
 [B<-chainCApath> I<directory>]
 [B<-chainCAfile> I<filename>]
+[B<-chainCAstore> I<uri>]
 [B<-requestCAfile> I<filename>]
 [B<-dane_tlsa_domain> I<domain>]
 [B<-dane_tlsa_rrdata> I<rrdata>]
@@ -303,6 +304,10 @@ information.
 A file containing trusted certificates to use when attempting to build the
 client certificate chain.
 
+=item B<-chainCAstore> I<uri>
+
+The URI to use when attempting to build the client certificate chain.
+
 =item B<-requestCAfile> I<file>
 
 A file containing a list of certificates whose subject names will be sent
@@ -807,7 +812,8 @@ L<openssl-ciphers(1)>,
 L<SSL_CONF_cmd(3)>,
 L<SSL_CTX_set_max_send_fragment(3)>,
 L<SSL_CTX_set_split_send_fragment(3)>,
-L<SSL_CTX_set_max_pipelines(3)>
+L<SSL_CTX_set_max_pipelines(3)>,
+L<ossl_store-file(7)>
 
 =head1 HISTORY
 
diff --git a/doc/man1/openssl-s_server.pod.in b/doc/man1/openssl-s_server.pod.in
index 638516695f..9e1c1d3e0e 100644
--- a/doc/man1/openssl-s_server.pod.in
+++ b/doc/man1/openssl-s_server.pod.in
@@ -61,6 +61,8 @@ B<openssl> B<s_server>
 [B<-dcert_chain> I<infile>]
 [B<-chainCApath> I<dir>]
 [B<-verifyCApath> I<dir>]
+[B<-chainCAstore> I<uri>]
+[B<-verifyCAstore> I<uri>]
 [B<-no_cache>]
 [B<-ext_cache>]
 [B<-verify_return_error>]
@@ -369,6 +371,16 @@ information.
 A file containing trusted certificates to use when attempting to build the
 server certificate chain.
 
+=item B<-chainCAstore> I<uri>
+
+The URI to a store to use for building the chain provided to the client.
+The URI may indicate a single certificate, as well as a collection of
+them.
+With URIs in the C<file:> scheme, this acts as B<-chainCAfile> or
+B<-chainCApath>, depending on if the URI indicates a directory or a
+single file.
+See L<ossl_store-file(7)> for more information on the C<file:> scheme.
+
 =item B<-nocert>
 
 If this option is set then no certificate is used. This restricts the
@@ -810,7 +822,8 @@ L<openssl-ciphers(1)>,
 L<SSL_CONF_cmd(3)>,
 L<SSL_CTX_set_max_send_fragment(3)>,
 L<SSL_CTX_set_split_send_fragment(3)>,
-L<SSL_CTX_set_max_pipelines(3)>
+L<SSL_CTX_set_max_pipelines(3)>,
+L<ossl_store-file(7)>
 
 =head1 HISTORY
 
diff --git a/doc/man1/openssl-s_time.pod.in b/doc/man1/openssl-s_time.pod.in
index 737424da87..18e243146e 100644
--- a/doc/man1/openssl-s_time.pod.in
+++ b/doc/man1/openssl-s_time.pod.in
@@ -87,12 +87,6 @@ I<option> argument can be a single option or multiple options separated by
 commas.  Alternatively the B<-nameopt> switch may be used more than once to
 set multiple options. See the L<openssl-x509(1)> manual page for details.
 
-=item B<-CApath> I<directory>
-
-The directory to use for server certificate verification. This directory
-must be in "hash format", see L<openssl-verify(1)> for more information.
-These are also used when building the client certificate chain.
-
 =item B<-new>
 
 Performs the timing test using a new session ID for each connection.
@@ -193,7 +187,8 @@ fails.
 L<openssl(1)>,
 L<openssl-s_client(1)>,
 L<openssl-s_server(1)>,
-L<openssl-ciphers(1)>
+L<openssl-ciphers(1)>,
+L<ossl_store-file(7)>
 
 =head1 COPYRIGHT
 
diff --git a/doc/man1/openssl-smime.pod.in b/doc/man1/openssl-smime.pod.in
index 4e07af3860..0f1c9bdf00 100644
--- a/doc/man1/openssl-smime.pod.in
+++ b/doc/man1/openssl-smime.pod.in
@@ -479,6 +479,10 @@ No revocation checking is done on the signer's certificate.
 The current code can only handle S/MIME v2 messages, the more complex S/MIME v3
 structures may cause parsing errors.
 
+=head1 SEE ALSO
+
+L<ossl_store-file(7)>
+
 =head1 HISTORY
 
 The use of multiple B<-signer> options and the B<-resign> command were first
diff --git a/doc/man1/openssl-ts.pod.in b/doc/man1/openssl-ts.pod.in
index d9354b2ab7..035763260d 100644
--- a/doc/man1/openssl-ts.pod.in
+++ b/doc/man1/openssl-ts.pod.in
@@ -52,6 +52,7 @@ B<-verify>
 [B<-token_in>]
 [B<-CApath> I<trusted_cert_path>]
 [B<-CAfile> I<trusted_certs.pem>]
+[B<-CAstore> I<trusted_certs_uri>]
 [B<-untrusted> I<cert_file.pem>]
 [I<verify options>]
 
@@ -352,10 +353,12 @@ This flag can be used together with the B<-in> option and indicates
 that the input is a DER encoded timestamp token (ContentInfo) instead
 of a timestamp response (TimeStampResp). (Optional)
 
-=item B<-CAfile> I<file>, B<-CApath> I<dir>
+=item B<-CAfile> I<file>, B<-CApath> I<dir>, B<-CAstore> I<uri>
 
 See L<openssl(1)/Trusted Certificate Options> for more information.
 
+At least one of B<-CApath>, B<-CAfile> or B<-CAstore> must be specified.
+
 =item B<-untrusted> I<cert_file.pem>
 
 Set of additional untrusted certificates in PEM format which may be
@@ -649,7 +652,8 @@ L<openssl-req(1)>,
 L<openssl-x509(1)>,
 L<openssl-ca(1)>,
 L<openssl-genrsa(1)>,
-L<config(5)>
+L<config(5)>,
+L<ossl_store-file(7)>
 
 =head1 COPYRIGHT
 
diff --git a/doc/man1/openssl-verify.pod.in b/doc/man1/openssl-verify.pod.in
index 72ef98cc85..9a92cb6f32 100644
--- a/doc/man1/openssl-verify.pod.in
+++ b/doc/man1/openssl-verify.pod.in
@@ -190,8 +190,8 @@ P-256 and P-384.
 =item B<-trusted_first>
 
 When constructing the certificate chain, use the trusted certificates specified
-via B<-CAfile>, B<-CApath> or B<-trusted> before any certificates specified via
-B<-untrusted>.
+via B<-CAfile>, B<-CApath>, B<-CAstore> or B<-trusted> before any certificates
+specified via B<-untrusted>.
 This can be useful in environments with Bridge or Cross-Certified CAs.
 As of OpenSSL 1.1.0 this option is on by default and cannot be disabled.
 
@@ -222,9 +222,9 @@ consulted.
 That is, the only trust-anchors are those listed in I<file>.
 This option can be specified more than once to include trusted certificates
 from multiple I<file>s.
-This option implies the B<-no-CAfile> and B<-no-CApath> options.
-This option cannot be used in combination with either of the B<-CAfile> or
-B<-CApath> options.
+This option implies the B<-no-CAfile>, B<-no-CApath> and B<-no-CAstore> options.
+This option cannot be used in combination with any of the B<-CAfile>,
+B<-CApath> or B<-CAstore> options.
 
 =item B<-use_deltas>
 
@@ -743,8 +743,9 @@ Although the issuer checks are a considerable improvement over the old
 technique they still suffer from limitations in the underlying X509_LOOKUP
 API. One consequence of this is that trusted certificates with matching
 subject name must either appear in a file (as specified by the B<-CAfile>
-option) or a directory (as specified by B<-CApath>). If they occur in
-both then only the certificates in the file will be recognised.
+option), a directory (as specified by B<-CApath>), or a store (as specified
+by B<-CAstore>). If they occur in more than one location then only the
+certificates in the file will be recognised.
 
 Previous versions of OpenSSL assume certificates with matching subject
 name are identical and mishandled them.
@@ -756,7 +757,8 @@ B<X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY> error codes.
 =head1 SEE ALSO
 
 L<openssl(1)>,
-L<openssl-x509(1)>
+L<openssl-x509(1)>,
+L<ossl_store-file(7)>
 
 =head1 HISTORY
 
diff --git a/doc/man1/openssl.pod b/doc/man1/openssl.pod
index 3198f90e22..e13a6969b7 100644
--- a/doc/man1/openssl.pod
+++ b/doc/man1/openssl.pod
@@ -700,6 +700,23 @@ See L<openssl-rehash(1)> for information on creating this type of directory.
 
 Do not use the default directory of trusted certificates.
 
+=item B<-CAstore> I<uri>
+
+Use I<uri> as a store of trusted CA certificates.  The URI may
+indicate a single certificate, as well as a collection of them.
+With URIs in the C<file:> scheme, this acts as B<-CAfile> or
+B<-CApath>, depending on if the URI indicates a single file or
+directory.
+See L<ossl_store-file(7)> for more information on the C<file:> scheme.
+
+These certificates are also used when building the server certificate
+chain (for example with L<openssl-s_server(1)>) or client certificate
+chain (for example with L<openssl-s_time(1)>).
+
+=item B<-no-CAstore>
+
+Do not use the default store.
+
 =back
 
 =head2 Random State Options
diff --git a/doc/man3/SSL_CTX_load_verify_locations.pod b/doc/man3/SSL_CTX_load_verify_locations.pod
index b955c60eed..3ee0f96345 100644
--- a/doc/man3/SSL_CTX_load_verify_locations.pod
+++ b/doc/man3/SSL_CTX_load_verify_locations.pod
@@ -2,36 +2,52 @@
 
 =head1 NAME
 
-SSL_CTX_load_verify_locations, SSL_CTX_set_default_verify_paths,
-SSL_CTX_set_default_verify_dir, SSL_CTX_set_default_verify_file - set
-default locations for trusted CA certificates
+SSL_CTX_load_verify_dir, SSL_CTX_load_verify_file,
+SSL_CTX_load_verify_store, SSL_CTX_set_default_verify_paths,
+SSL_CTX_set_default_verify_dir, SSL_CTX_set_default_verify_file,
+SSL_CTX_set_default_verify_store, SSL_CTX_load_verify_locations
+- set default locations for trusted CA certificates
 
 =head1 SYNOPSIS
 
  #include <openssl/ssl.h>
 
- int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,
-                                   const char *CApath);
+ int SSL_CTX_load_verify_dir(SSL_CTX *ctx, const char *CApath);
+ int SSL_CTX_load_verify_file(SSL_CTX *ctx, const char *CAfile);
+ int SSL_CTX_load_verify_store(SSL_CTX *ctx, const char *CAstore);
 
  int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx);
 
  int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx);
-
  int SSL_CTX_set_default_verify_file(SSL_CTX *ctx);
+ int SSL_CTX_set_default_verify_store(SSL_CTX *ctx);
+
+Deprecated since OpenSSL 3.0, can be hidden entirely by defining
+B<OPENSSL_API_COMPAT> with a suitable version value, see
+L<openssl_user_macros(7)>:
+
+ int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,
+                                   const char *CApath);
 
 =head1 DESCRIPTION
 
-SSL_CTX_load_verify_locations() specifies the locations for B<ctx>, at
-which CA certificates for verification purposes are located. The certificates
-available via B<CAfile> and B<CApath> are trusted.
+SSL_CTX_load_verify_dir(), SSL_CTX_load_verify_file(),
+SSL_CTX_load_verify_store() specifies the locations for B<ctx>, at
+which CA certificates for verification purposes are located. The
+certificates available via B<CAfile>, B<CApath> and B<CAstore> are
+trusted.
 
 SSL_CTX_set_default_verify_paths() specifies that the default locations from
-which CA certificates are loaded should be used. There is one default directory
-and one default file. The default CA certificates directory is called "certs" in
-the default OpenSSL directory. Alternatively the SSL_CERT_DIR environment
-variable can be defined to override this location. The default CA certificates
-file is called "cert.pem" in the default OpenSSL directory. Alternatively the
-SSL_CERT_FILE environment variable can be defined to override this location.
+which CA certificates are loaded should be used. There is one default directory,
+one default file and one default store.
+The default CA certificates directory is called "certs" in the default OpenSSL
+directory, and this is also the default store.
+Alternatively the SSL_CERT_DIR environment variable can be defined to
+override this location.
+The default CA certificates file is called "cert.pem" in the default
+OpenSSL directory.
+Alternatively the SSL_CERT_FILE environment variable can be defined to
+override this location.
 
 SSL_CTX_set_default_verify_dir() is similar to
 SSL_CTX_set_default_verify_paths() except that just the default directory is
@@ -41,6 +57,10 @@ SSL_CTX_set_default_verify_file() is similar to
 SSL_CTX_set_default_verify_paths() except that just the default file is
 used.
 
+SSL_CTX_set_default_verify_store() is similar to
+SSL_CTX_set_default_verify_paths() except that just the default store is
+used.
+
 =head1 NOTES
 
 If B<CAfile> is not NULL, it points to a file of CA certificates in PEM
@@ -78,6 +98,11 @@ matching the parameters is found, the verification process will be performed;
 no other certificates for the same parameters will be searched in case of
 failure.
 
+If B<CAstore> is not NULL, it's a URI for to a store, which may
+represent a single container or a whole catalogue of containers.
+Apart from the B<CAstore> not necessarily being a local file or
+directory, it's generally treated the same way as a B<CApath>.
+
 In server mode, when requesting a client certificate, the server must send
 the list of CAs of which it will accept client certificates. This list
 is not influenced by the contents of B<CAfile> or B<CApath> and must
diff --git a/doc/man3/X509_LOOKUP_hash_dir.pod b/doc/man3/X509_LOOKUP_hash_dir.pod
index a9b837a308..30fbda8e03 100644
--- a/doc/man3/X509_LOOKUP_hash_dir.pod
+++ b/doc/man3/X509_LOOKUP_hash_dir.pod
@@ -2,7 +2,7 @@
 
 =head1 NAME
 
-X509_LOOKUP_hash_dir, X509_LOOKUP_file,
+X509_LOOKUP_hash_dir, X509_LOOKUP_file, X509_LOOKUP_store,
 X509_load_cert_file,
 X509_load_crl_file,
 X509_load_cert_crl_file - Default OpenSSL certificate
@@ -14,6 +14,7 @@ lookup methods
 
  X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void);
  X509_LOOKUP_METHOD *X509_LOOKUP_file(void);
+ X509_LOOKUP_METHOD *X509_LOOKUP_store(void);
 
  int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type);
  int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type);
@@ -111,10 +112,24 @@ Note that the hash algorithm used for subject name hashing changed in OpenSSL
 OpenSSL includes a L<rehash(1)> utility which creates symlinks with correct
 hashed names for all files with .pem suffix in a given directory.
 
+=head2 OSSL_STORE Method
+
+B<X509_LOOKUP_store> is a method that allows access to any store of
+certificates and CRLs through any loader supported by
+L<OSSL_STORE(3)>.
+It works with the help of URIs, which can be direct references to
+certificates or CRLs, but can also be references to catalogues of such
+objects (that behave like directories).
+
+This method overlaps the L</File Method> and L</Hashed Directory Method>
+because of the 'file:' scheme loader.
+It does no caching of its own, but can use a caching L<OSSL_STORE(3)>
+loader, and therefore depends on the loader's capability.
+
 =head1 RETURN VALUES
 
-X509_LOOKUP_hash_dir() and X509_LOOKUP_file() always return a valid
-B<X509_LOOKUP_METHOD> structure.
+X509_LOOKUP_hash_dir(), X509_LOOKUP_file() and X509_LOOKUP_store()
+always return a valid B<X509_LOOKUP_METHOD> structure.
 
 X509_load_cert_file(), X509_load_crl_file() and X509_load_cert_crl_file() return
 the number of loaded objects or 0 on error.
@@ -126,10 +141,15 @@ L<X509_STORE_load_locations(3)>,
 L<X509_store_add_lookup(3)>,
 L<SSL_CTX_load_verify_locations(3)>,
 L<X509_LOOKUP_meth_new(3)>,
+L<OSSL_STORE(3)>
+
+=head1 HISTORY
+
+B<X509_LOOKUP_store> was added in OpenSSL 3.0.
 
 =head1 COPYRIGHT
 
-Copyright 2015-2018 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2015-2019 The OpenSSL Project Authors. All Rights Reserved.
 
 Licensed under the Apache License 2.0 (the "License").  You may not use
 this file except in compliance with the License.  You can obtain a copy
diff --git a/doc/man3/X509_STORE_add_cert.pod b/doc/man3/X509_STORE_add_cert.pod
index c0940b96ed..dd3d389e22 100644
--- a/doc/man3/X509_STORE_add_cert.pod
+++ b/doc/man3/X509_STORE_add_cert.pod
@@ -4,8 +4,9 @@
 
 X509_STORE_add_cert, X509_STORE_add_crl, X509_STORE_set_depth,
 X509_STORE_set_flags, X509_STORE_set_purpose, X509_STORE_set_trust,
-X509_STORE_load_locations,
-X509_STORE_set_default_paths
+X509_STORE_load_file, X509_STORE_load_path, X509_STORE_load_store,
+X509_STORE_set_default_paths,
+X509_STORE_load_locations
 - X509_STORE manipulation
 
 =head1 SYNOPSIS
@@ -19,9 +20,15 @@ X509_STORE_set_default_paths
  int X509_STORE_set_purpose(X509_STORE *ctx, int purpose);
  int X509_STORE_set_trust(X509_STORE *ctx, int trust);
 
+ int X509_STORE_set_default_paths(X509_STORE *ctx);
+ int X509_STORE_load_file(X509_STORE *ctx, const char *file);
+ int X509_STORE_load_path(X509_STORE *ctx, const char *dir);
+ int X509_STORE_load_store(X509_STORE *ctx, const char *uri);
+
+Deprecated:
+
  int X509_STORE_load_locations(X509_STORE *ctx,
                                const char *file, const char *dir);
- int X509_STORE_set_default_paths(X509_STORE *ctx);
 
 =head1 DESCRIPTION
 
@@ -65,11 +72,21 @@ for the corresponding values used in certificate chain validation.  Their
 behavior is documented in the corresponding B<X509_VERIFY_PARAM> manual
 pages, e.g., L<X509_VERIFY_PARAM_set_depth(3)>.
 
-X509_STORE_load_locations() loads trusted certificate(s) into an
-B<X509_STORE> from a given file and/or directory path.  It is permitted
-to specify just a file, just a directory, or both paths.  The certificates
-in the directory must be in hashed form, as documented in
-L<X509_LOOKUP_hash_dir(3)>.
+X509_STORE_load_file() loads trusted certificate(s) into an
+B<X509_STORE> from a given file.
+
+X509_STORE_load_path() loads trusted certificate(s) into an
+B<X509_STORE> from a given directory path.
+The certificates in the directory must be in hashed form, as
+documented in L<X509_LOOKUP_hash_dir(3)>.
+
+X509_STORE_load_store() loads trusted certificate(s) into an
+B<X509_STORE> from a store at a given URI.
+
+X509_STORE_load_locations() combines X509_STORE_load_file() and
+X509_STORE_load_dir() for a given file and/or directory path.
+It is permitted to specify just a file, just a directory, or both
+paths.
 
 X509_STORE_set_default_paths() is somewhat misnamed, in that it does not
 set what default paths should be used for loading certificates.  Instead,
@@ -80,8 +97,10 @@ paths.
 
 X509_STORE_add_cert(), X509_STORE_add_crl(), X509_STORE_set_depth(),
 X509_STORE_set_flags(), X509_STORE_set_purpose(),
-X509_STORE_set_trust(), X509_STORE_load_locations(), and
-X509_STORE_set_default_paths() return 1 on success or 0 on failure.
+X509_STORE_set_trust(), X509_STORE_load_file(),
+X509_STORE_load_path(), X509_STORE_load_store(),
+X509_STORE_load_locations(), and X509_STORE_set_default_paths() return
+1 on success or 0 on failure.
 
 =head1 SEE ALSO
 
diff --git a/doc/perlvars.pm b/doc/perlvars.pm
index ca62ca5ce9..da61f77897 100644
--- a/doc/perlvars.pm
+++ b/doc/perlvars.pm
@@ -82,9 +82,12 @@ $OpenSSL::safe::opt_trust_synopsis = ""
 . "[B<-CAfile> I<file>]\n"
 . "[B<-no-CAfile>]\n"
 . "[B<-CApath> I<dir>]\n"
-. "[B<-no-CApath>]";
+. "[B<-no-CApath>]\n"
+. "[B<-CAstore> I<uri>]\n"
+. "[B<-no-CAstore>]";
 $OpenSSL::safe::opt_trust_item = ""
-. "=item B<-CAfile> I<file>, B<-no-CAfile>, B<-CApath> I<dir>, B<-no-CApath>\n"
+. "=item B<-CAfile> I<file>, B<-no-CAfile>, B<-CApath> I<dir>, B<-no-CApath>,\n"
+. "B<-CAstore> I<uri>, B<-no-CAstore>\n"
 . "\n"
 . "See L<openssl(1)/Trusted Certificate Options> for details.";
 
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 35477d9cb7..dbf1b48f14 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -598,6 +598,7 @@ typedef int (*SSL_async_callback_fn)(SSL *s, void *arg);
 # define SSL_CONF_TYPE_FILE              0x2
 # define SSL_CONF_TYPE_DIR               0x3
 # define SSL_CONF_TYPE_NONE              0x4
+# define SSL_CONF_TYPE_STORE             0x5
 
 /* Maximum length of the application-controlled segment of a a TLSv1.3 cookie */
 # define SSL_COOKIE_LENGTH                       4096
@@ -1626,6 +1627,8 @@ __owur int SSL_add_file_cert_subjects_to_stack(STACK_OF(X509_NAME) *stackCAs,
                                                const char *file);
 int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stackCAs,
                                        const char *dir);
+int SSL_add_store_cert_subjects_to_stack(STACK_OF(X509_NAME) *stackCAs,
+                                       const char *uri);
 
 # if !OPENSSL_API_1_1_0
 #  define SSL_load_error_strings() \
@@ -2013,8 +2016,13 @@ __owur int SSL_client_version(const SSL *s);
 __owur int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx);
 __owur int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx);
 __owur int SSL_CTX_set_default_verify_file(SSL_CTX *ctx);
-__owur int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,
-                                         const char *CApath);
+__owur int SSL_CTX_set_default_verify_store(SSL_CTX *ctx);
+__owur int SSL_CTX_load_verify_file(SSL_CTX *ctx, const char *CAfile);
+__owur int SSL_CTX_load_verify_dir(SSL_CTX *ctx, const char *CApath);
+__owur int SSL_CTX_load_verify_store(SSL_CTX *ctx, const char *CAstore);
+DEPRECATEDIN_3(__owur int SSL_CTX_load_verify_locations(SSL_CTX *ctx,
+                                                        const char *CAfile,
+                                                        const char *CApath))
 # define SSL_get0_session SSL_get_session/* just peek at pointer */
 __owur SSL_SESSION *SSL_get_session(const SSL *ssl);
 __owur SSL_SESSION *SSL_get1_session(SSL *ssl); /* obtain a reference count */
diff --git a/include/openssl/store.h b/include/openssl/store.h
index 846923e0ea..8cceff7dbb 100644
--- a/include/openssl/store.h
+++ b/include/openssl/store.h
@@ -191,7 +191,7 @@ void OSSL_STORE_SEARCH_free(OSSL_STORE_SEARCH *search);
 
 /* Search term accessors */
 int OSSL_STORE_SEARCH_get_type(const OSSL_STORE_SEARCH *criterion);
-X509_NAME *OSSL_STORE_SEARCH_get0_name(OSSL_STORE_SEARCH *criterion);
+X509_NAME *OSSL_STORE_SEARCH_get0_name(const OSSL_STORE_SEARCH *criterion);
 const ASN1_INTEGER *OSSL_STORE_SEARCH_get0_serial(const OSSL_STORE_SEARCH
                                                   *criterion);
 const unsigned char *OSSL_STORE_SEARCH_get0_bytes(const OSSL_STORE_SEARCH
@@ -204,7 +204,7 @@ const EVP_MD *OSSL_STORE_SEARCH_get0_digest(const OSSL_STORE_SEARCH *criterion);
  * to the loading channel.  This MUST happen before the first OSSL_STORE_load().
  */
 int OSSL_STORE_expect(OSSL_STORE_CTX *ctx, int expected_type);
-int OSSL_STORE_find(OSSL_STORE_CTX *ctx, OSSL_STORE_SEARCH *search);
+int OSSL_STORE_find(OSSL_STORE_CTX *ctx, const OSSL_STORE_SEARCH *search);
 
 
 /*-
@@ -236,7 +236,7 @@ typedef int (*OSSL_STORE_expect_fn)(OSSL_STORE_LOADER_CTX *ctx, int expected);
 int OSSL_STORE_LOADER_set_expect(OSSL_STORE_LOADER *loader,
                                  OSSL_STORE_expect_fn expect_function);
 typedef int (*OSSL_STORE_find_fn)(OSSL_STORE_LOADER_CTX *ctx,
-                                  OSSL_STORE_SEARCH *criteria);
+                                  const OSSL_STORE_SEARCH *criteria);
 int OSSL_STORE_LOADER_set_find(OSSL_STORE_LOADER *loader,
                                OSSL_STORE_find_fn find_function);
 typedef OSSL_STORE_INFO *(*OSSL_STORE_load_fn)(OSSL_STORE_LOADER_CTX *ctx,
diff --git a/include/openssl/trace.h b/include/openssl/trace.h
index 31e6fcc3f5..f71d9fb4ca 100644
--- a/include/openssl/trace.h
+++ b/include/openssl/trace.h
@@ -50,7 +50,8 @@ extern "C" {
 # define OSSL_TRACE_CATEGORY_X509V3_POLICY      11
 # define OSSL_TRACE_CATEGORY_BN_CTX             12
 # define OSSL_TRACE_CATEGORY_CMP                13
-# define OSSL_TRACE_CATEGORY_NUM                14
+# define OSSL_TRACE_CATEGORY_STORE              14
+# define OSSL_TRACE_CATEGORY_NUM                15
 
 /* Returns the trace category number for the given |name| */
 int OSSL_trace_get_category_num(const char *name);
diff --git a/include/openssl/x509_vfy.h b/include/openssl/x509_vfy.h
index e16ad1d7c0..8b5f3f6dd6 100644
--- a/include/openssl/x509_vfy.h
+++ b/include/openssl/x509_vfy.h
@@ -95,6 +95,8 @@ void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth);
 
 # define X509_L_FILE_LOAD        1
 # define X509_L_ADD_DIR          2
+# define X509_L_ADD_STORE        3
+# define X509_L_LOAD_STORE       4
 
 # define X509_LOOKUP_load_file(x,name,type) \
                 X509_LOOKUP_ctrl((x),X509_L_FILE_LOAD,(name),(long)(type),NULL)
@@ -102,6 +104,12 @@ void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth);
 # define X509_LOOKUP_add_dir(x,name,type) \
                 X509_LOOKUP_ctrl((x),X509_L_ADD_DIR,(name),(long)(type),NULL)
 
+# define X509_LOOKUP_add_store(x,name) \
+                X509_LOOKUP_ctrl((x),X509_L_ADD_STORE,(name),0,NULL)
+
+# define X509_LOOKUP_load_store(x,name) \
+                X509_LOOKUP_ctrl((x),X509_L_LOAD_STORE,(name),0,NULL)
+
 # define         X509_V_OK                                       0
 # define         X509_V_ERR_UNSPECIFIED                          1
 # define         X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT            2
@@ -383,6 +391,7 @@ X509_STORE_CTX_cleanup_fn X509_STORE_CTX_get_cleanup(X509_STORE_CTX *ctx);
 X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m);
 X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void);
 X509_LOOKUP_METHOD *X509_LOOKUP_file(void);
+X509_LOOKUP_METHOD *X509_LOOKUP_store(void);
 
 typedef int (*X509_LOOKUP_ctrl_fn)(X509_LOOKUP *ctx, int cmd, const char *argc,
                                    long argl, char **ret);
@@ -488,8 +497,11 @@ void *X509_LOOKUP_get_method_data(const X509_LOOKUP *ctx);
 X509_STORE *X509_LOOKUP_get_store(const X509_LOOKUP *ctx);
 int X509_LOOKUP_shutdown(X509_LOOKUP *ctx);
 
-int X509_STORE_load_locations(X509_STORE *ctx,
-                              const char *file, const char *dir);
+int X509_STORE_load_file(X509_STORE *ctx, const char *file);
+int X509_STORE_load_path(X509_STORE *ctx, const char *path);
+int X509_STORE_load_store(X509_STORE *ctx, const char *store);
+DEPRECATEDIN_3(int X509_STORE_load_locations(X509_STORE *ctx, const char *file,
+                                             const char *dir))
 int X509_STORE_set_default_paths(X509_STORE *ctx);
 
 #define X509_STORE_CTX_get_ex_new_index(l, p, newf, dupf, freef) \
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index e3ad6a55ee..28fbdf8e65 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -15,6 +15,7 @@
 #include "internal/o_dir.h"
 #include <openssl/bio.h>
 #include <openssl/pem.h>
+#include <openssl/store.h>
 #include <openssl/x509v3.h>
 #include <openssl/dh.h>
 #include <openssl/bn.h>
@@ -782,6 +783,79 @@ int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack,
     return ret;
 }
 
+/**
+ * Add a container of certs to a stack.
+ * \param stack the stack to add to.
+ * \param file the file to add from. All certs in this file that are not
+ * already in the stack will be added.
+ * \return 1 for success, 0 for failure. Note that in the case of failure some
+ * certs may have been added to \c stack.
+ */
+
+static int add_uris_recursive(STACK_OF(X509_NAME) *stack,
+                              const char *uri, int depth)
+{
+    int ok = 1;
+    OSSL_STORE_CTX *ctx = NULL;
+    X509 *x = NULL;
+    X509_NAME *xn = NULL;
+
+    if ((ctx = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL)) == NULL)
+        goto err;
+
+    while (!OSSL_STORE_eof(ctx) && !OSSL_STORE_error(ctx)) {
+        OSSL_STORE_INFO *info = OSSL_STORE_load(ctx);
+        int infotype = info == 0 ? 0 : OSSL_STORE_INFO_get_type(info);
+
+        if (info == NULL)
+            continue;
+
+        if (infotype == OSSL_STORE_INFO_NAME) {
+            /*
+             * This is an entry in the "directory" represented by the current
+             * uri.  if |depth| allows, dive into it.
+             */
+            if (depth == 0)
+                ok = add_uris_recursive(stack, uri, depth - 1);
+        } else if (infotype == OSSL_STORE_INFO_CERT) {
+            if ((x = OSSL_STORE_INFO_get0_CERT(info)) == NULL
+                || (xn = X509_get_subject_name(x)) == NULL
+                || (xn = X509_NAME_dup(xn)) == NULL)
+                goto err;
+            if (sk_X509_NAME_find(stack, xn) >= 0) {
+                /* Duplicate. */
+                X509_NAME_free(xn);
+            } else if (!sk_X509_NAME_push(stack, xn)) {
+                X509_NAME_free(xn);
+                goto err;
+            }
+        }
+
+        OSSL_STORE_INFO_free(info);
+    }
+
+    ERR_clear_error();
+    goto done;
+
+ err:
+    ok = 0;
+ done:
+    OSSL_STORE_close(ctx);
+
+    return ok;
+}
+
+int SSL_add_store_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack,
+                                         const char *store)
+{
+    int (*oldcmp) (const X509_NAME *const *a, const X509_NAME *const *b)
+        = sk_X509_NAME_set_cmp_func(stack, xname_sk_cmp);
+    int ret = add_uris_recursive(stack, store, 1);
+
+    (void)sk_X509_NAME_set_cmp_func(stack, oldcmp);
+    return ret;
+}
+
 /* Build a certificate chain for current certificate */
 int ssl_build_cert_chain(SSL *s, SSL_CTX *ctx, int flags)
 {
diff --git a/ssl/ssl_conf.c b/ssl/ssl_conf.c
index 40db39b2e1..cccda866eb 100644
--- a/ssl/ssl_conf.c
+++ b/ssl/ssl_conf.c
@@ -455,10 +455,12 @@ static int cmd_ServerInfoFile(SSL_CONF_CTX *cctx, const char *value)
 }
 
 static int do_store(SSL_CONF_CTX *cctx,
-                    const char *CAfile, const char *CApath, int verify_store)
+                    const char *CAfile, const char *CApath, const char *CAstore,
+                    int verify_store)
 {
     CERT *cert;
     X509_STORE **st;
+
     if (cctx->ctx)
         cert = cctx->ctx->cert;
     else if (cctx->ssl)
@@ -471,27 +473,44 @@ static int do_store(SSL_CONF_CTX *cctx,
         if (*st == NULL)
             return 0;
     }
-    return X509_STORE_load_locations(*st, CAfile, CApath) > 0;
+
+    if (CAfile != NULL && !X509_STORE_load_file(*st, CAfile))
+        return 0;
+    if (CApath != NULL && !X509_STORE_load_path(*st, CApath))
+        return 0;
+    if (CAstore != NULL && !X509_STORE_load_store(*st, CAstore))
+        return 0;
+    return 1;
 }
 
 static int cmd_ChainCAPath(SSL_CONF_CTX *cctx, const char *value)
 {
-    return do_store(cctx, NULL, value, 0);
+    return do_store(cctx, NULL, value, NULL, 0);
 }
 
 static int cmd_ChainCAFile(SSL_CONF_CTX *cctx, const char *value)
 {
-    return do_store(cctx, value, NULL, 0);
+    return do_store(cctx, value, NULL, NULL, 0);
+}
+
+static int cmd_ChainCAStore(SSL_CONF_CTX *cctx, const char *value)
+{
+    return do_store(cctx, NULL, NULL, value, 0);
 }
 
 static int cmd_VerifyCAPath(SSL_CONF_CTX *cctx, const char *value)
 {
-    return do_store(cctx, NULL, value, 1);
+    return do_store(cctx, NULL, value, NULL, 1);
 }
 
 static int cmd_VerifyCAFile(SSL_CONF_CTX *cctx, const char *value)
 {
-    return do_store(cctx, value, NULL, 1);
+    return do_store(cctx, value, NULL, NULL, 1);
+}
+
+static int cmd_VerifyCAStore(SSL_CONF_CTX *cctx, const char *value)
+{
+    return do_store(cctx, NULL, NULL, value, 1);
 }
 
 static int cmd_RequestCAFile(SSL_CONF_CTX *cctx, const char *value)
@@ -522,6 +541,20 @@ static int cmd_ClientCAPath(SSL_CONF_CTX *cctx, const char *value)
     return cmd_RequestCAPath(cctx, value);
 }
 
+static int cmd_RequestCAStore(SSL_CONF_CTX *cctx, const char *value)
+{
+    if (cctx->canames == NULL)
+        cctx->canames = sk_X509_NAME_new_null();
+    if (cctx->canames == NULL)
+        return 0;
+    return SSL_add_store_cert_subjects_to_stack(cctx->canames, value);
+}
+
+static int cmd_ClientCAStore(SSL_CONF_CTX *cctx, const char *value)
+{
+    return cmd_RequestCAStore(cctx, value);
+}
+
 #ifndef OPENSSL_NO_DH
 static int cmd_DHParameters(SSL_CONF_CTX *cctx, const char *value)
 {
@@ -651,10 +684,14 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = {
                  SSL_CONF_TYPE_DIR),
     SSL_CONF_CMD(ChainCAFile, "chainCAfile", SSL_CONF_FLAG_CERTIFICATE,
                  SSL_CONF_TYPE_FILE),
+    SSL_CONF_CMD(ChainCAStore, "chainCAstore", SSL_CONF_FLAG_CERTIFICATE,
+                 SSL_CONF_TYPE_STORE),
     SSL_CONF_CMD(VerifyCAPath, "verifyCApath", SSL_CONF_FLAG_CERTIFICATE,
                  SSL_CONF_TYPE_DIR),
     SSL_CONF_CMD(VerifyCAFile, "verifyCAfile", SSL_CONF_FLAG_CERTIFICATE,
                  SSL_CONF_TYPE_FILE),
+    SSL_CONF_CMD(VerifyCAStore, "verifyCAstore", SSL_CONF_FLAG_CERTIFICATE,
+                 SSL_CONF_TYPE_STORE),
     SSL_CONF_CMD(RequestCAFile, "requestCAFile", SSL_CONF_FLAG_CERTIFICATE,
                  SSL_CONF_TYPE_FILE),
     SSL_CONF_CMD(ClientCAFile, NULL,
@@ -665,6 +702,11 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = {
     SSL_CONF_CMD(ClientCAPath, NULL,
                  SSL_CONF_FLAG_SERVER | SSL_CONF_FLAG_CERTIFICATE,
                  SSL_CONF_TYPE_DIR),
+    SSL_CONF_CMD(RequestCAStore, "requestCAStore", SSL_CONF_FLAG_CERTIFICATE,
+                 SSL_CONF_TYPE_STORE),
+    SSL_CONF_CMD(ClientCAStore, NULL,
+                 SSL_CONF_FLAG_SERVER | SSL_CONF_FLAG_CERTIFICATE,
+                 SSL_CONF_TYPE_STORE),
 #ifndef OPENSSL_NO_DH
     SSL_CONF_CMD(DHParameters, "dhparam",
                  SSL_CONF_FLAG_SERVER | SSL_CONF_FLAG_CERTIFICATE,
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 61c90218e3..2c901ff176 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -4181,10 +4181,13 @@ int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx)
     lookup = X509_STORE_add_lookup(ctx->cert_store, X509_LOOKUP_hash_dir());
     if (lookup == NULL)
         return 0;
+
+    /* We ignore errors, in case the directory doesn't exist */
+    ERR_set_mark();
+
     X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
 
-    /* Clear any errors if the default directory does not exist */
-    ERR_clear_error();
+    ERR_pop_to_mark();
 
     return 1;
 }
@@ -4197,19 +4200,62 @@ int SSL_CTX_set_default_verify_file(SSL_CTX *ctx)
     if (lookup == NULL)
         return 0;
 
+    /* We ignore errors, in case the directory doesn't exist */
+    ERR_set_mark();
+
     X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT);
 
-    /* Clear any errors if the default file does not exist */
-    ERR_clear_error();
+    ERR_pop_to_mark();
 
     return 1;
 }
 
+int SSL_CTX_set_default_verify_store(SSL_CTX *ctx)
+{
+    X509_LOOKUP *lookup;
+
+    lookup = X509_STORE_add_lookup(ctx->cert_store, X509_LOOKUP_store());
+    if (lookup == NULL)
+        return 0;
+
+    /* We ignore errors, in case the directory doesn't exist */
+    ERR_set_mark();
+
+    X509_LOOKUP_add_store(lookup, NULL);
+
+    ERR_pop_to_mark();
+
+    return 1;
+}
+
+int SSL_CTX_load_verify_file(SSL_CTX *ctx, const char *CAfile)
+{
+    return X509_STORE_load_file(ctx->cert_store, CAfile);
+}
+
+int SSL_CTX_load_verify_dir(SSL_CTX *ctx, const char *CApath)
+{
+    return X509_STORE_load_path(ctx->cert_store, CApath);
+}
+
+int SSL_CTX_load_verify_store(SSL_CTX *ctx, const char *CAstore)
+{
+    return X509_STORE_load_store(ctx->cert_store, CAstore);
+}
+
+#if OPENSSL_API_LEVEL < 3
 int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,
                                   const char *CApath)
 {
-    return X509_STORE_load_locations(ctx->cert_store, CAfile, CApath);
+    if (CAfile == NULL && CApath == NULL)
+        return 0;
+    if (CAfile != NULL && !SSL_CTX_load_verify_file(ctx, CAfile))
+        return 0;
+    if (CApath != NULL && !SSL_CTX_load_verify_dir(ctx, CApath))
+        return 0;
+    return 1;
 }
+#endif
 
 void SSL_set_info_callback(SSL *ssl,
                            void (*cb) (const SSL *ssl, int type, int val))
diff --git a/test/danetest.c b/test/danetest.c
index 26745f908e..2c228ecc52 100644
--- a/test/danetest.c
+++ b/test/danetest.c
@@ -393,7 +393,7 @@ static int run_tlsatest(void)
     if (!TEST_ptr(f = BIO_new_file(tlsafile, "r"))
             || !TEST_ptr(ctx = SSL_CTX_new(TLS_client_method()))
             || !TEST_int_gt(SSL_CTX_dane_enable(ctx), 0)
-            || !TEST_true(SSL_CTX_load_verify_locations(ctx, CAfile, NULL))
+            || !TEST_true(SSL_CTX_load_verify_file(ctx, CAfile))
             || !TEST_int_gt(SSL_CTX_dane_mtype_set(ctx, EVP_sha512(), 2, 1),
                             0)
             || !TEST_int_gt(SSL_CTX_dane_mtype_set(ctx, EVP_sha256(), 1, 2),
diff --git a/test/recipes/25-test_verify_store.t b/test/recipes/25-test_verify_store.t
new file mode 100644
index 0000000000..c8c57a7b2b
--- /dev/null
+++ b/test/recipes/25-test_verify_store.t
@@ -0,0 +1,118 @@
+#! /usr/bin/env perl
+# Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+use warnings;
+
+use OpenSSL::Test qw/:DEFAULT with bldtop_file srctop_file cmdstr/;
+use OpenSSL::Test::Utils;
+
+setup("test_verify_store");
+
+plan tests => 10;
+
+my $dummycnf = srctop_file("apps", "openssl.cnf");
+
+my $CAkey = "keyCA.ss";
+my $CAcert="certCA.ss";
+my $CAserial="certCA.srl";
+my $CAreq="reqCA.ss";
+my $CAconf=srctop_file("test","CAss.cnf");
+my $CAreq2="req2CA.ss";	# temp
+
+my $Uconf=srctop_file("test","Uss.cnf");
+my $Ukey="keyU.ss";
+my $Ureq="reqU.ss";
+my $Ucert="certU.ss";
+
+SKIP: {
+    req( 'make cert request',
+         qw(-new),
+         -config       => $CAconf,
+         -out          => $CAreq,
+         -keyout       => $CAkey );
+
+    skip 'failure', 8 unless
+        x509( 'convert request into self-signed cert',
+              qw(-req -CAcreateserial),
+              -in       => $CAreq,
+              -out      => $CAcert,
+              -signkey  => $CAkey,
+              -days     => 30,
+              -extfile  => $CAconf,
+              -extensions => 'v3_ca' );
+
+    skip 'failure', 7 unless
+        x509( 'convert cert into a cert request',
+              qw(-x509toreq),
+              -in       => $CAcert,
+              -out      => $CAreq2,
+              -signkey  => $CAkey );
+
+    skip 'failure', 6 unless
+        req( 'verify request 1',
+             qw(-verify -noout),
+             -config    => $dummycnf,
+             -in        => $CAreq );
+
+    skip 'failure', 5 unless
+        req( 'verify request 2',
+             qw(-verify -noout),
+             -config    => $dummycnf,
+             -in        => $CAreq2 );
+
+    skip 'failure', 4 unless
+        verify( 'verify signature',
+                -CAstore => $CAcert,
+                $CAcert );
+
+    skip 'failure', 3 unless
+        req( 'make a user cert request',
+             qw(-new),
+             -config  => $Uconf,
+             -out     => $Ureq,
+             -keyout  => $Ukey );
+
+    skip 'failure', 2 unless
+        x509( 'sign user cert request',
+              qw(-req -CAcreateserial),
+              -in     => $Ureq,
+              -out    => $Ucert,
+              -CA     => $CAcert,
+              -CAkey  => $CAkey,
+              -CAserial => $CAserial,
+              -days   => 30,
+              -extfile => $Uconf,
+              -extensions => 'v3_ee' )
+        && verify( undef,
+                   -CAstore => $CAcert,
+                   $Ucert );
+
+    skip 'failure', 0 unless
+        x509( 'Certificate details',
+              qw( -subject -issuer -startdate -enddate -noout),
+              -in     => $Ucert );
+}
+
+sub verify {
+    my $title = shift;
+
+    ok(run(app([qw(openssl verify), @_])), $title);
+}
+
+sub req {
+    my $title = shift;
+
+    ok(run(app([qw(openssl req), @_])), $title);
+}
+
+sub x509 {
+    my $title = shift;
+
+    ok(run(app([qw(openssl x509), @_])), $title);
+}
diff --git a/test/ssltest_old.c b/test/ssltest_old.c
index 971015c6e6..050470d33e 100644
--- a/test/ssltest_old.c
+++ b/test/ssltest_old.c
@@ -1492,12 +1492,15 @@ int main(int argc, char *argv[])
     (void)no_dhe;
 #endif
 
-    if ((!SSL_CTX_load_verify_locations(s_ctx, CAfile, CApath)) ||
-        (!SSL_CTX_set_default_verify_paths(s_ctx)) ||
-        (!SSL_CTX_load_verify_locations(s_ctx2, CAfile, CApath)) ||
-        (!SSL_CTX_set_default_verify_paths(s_ctx2)) ||
-        (!SSL_CTX_load_verify_locations(c_ctx, CAfile, CApath)) ||
-        (!SSL_CTX_set_default_verify_paths(c_ctx))) {
+    if (!(SSL_CTX_load_verify_file(s_ctx, CAfile)
+          || SSL_CTX_load_verify_dir(s_ctx, CApath))
+        || !SSL_CTX_set_default_verify_paths(s_ctx)
+        || !(SSL_CTX_load_verify_file(s_ctx2, CAfile)
+             || SSL_CTX_load_verify_dir(s_ctx2, CApath))
+        || !SSL_CTX_set_default_verify_paths(s_ctx2)
+        || !(SSL_CTX_load_verify_file(c_ctx, CAfile)
+             || SSL_CTX_load_verify_dir(c_ctx, CApath))
+        || !SSL_CTX_set_default_verify_paths(c_ctx)) {
         ERR_print_errors(bio_err);
     }
 
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 41ab92ac04..8a88b6e8bd 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -2122,7 +2122,7 @@ X509_EXTENSION_create_by_NID            2168	3_0_0	EXIST::FUNCTION:
 i2d_RSAPrivateKey                       2169	3_0_0	EXIST::FUNCTION:RSA
 d2i_CERTIFICATEPOLICIES                 2170	3_0_0	EXIST::FUNCTION:
 CMAC_CTX_get0_cipher_ctx                2171	3_0_0	EXIST::FUNCTION:CMAC
-X509_STORE_load_locations               2172	3_0_0	EXIST::FUNCTION:
+X509_STORE_load_locations               2172	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3
 OBJ_find_sigid_algs                     2173	3_0_0	EXIST::FUNCTION:
 TS_RESP_CTX_set_accuracy                2174	3_0_0	EXIST::FUNCTION:TS
 NETSCAPE_SPKI_get_pubkey                2175	3_0_0	EXIST::FUNCTION:
@@ -4853,3 +4853,7 @@ EVP_SIGNATURE_number                    4969	3_0_0	EXIST::FUNCTION:
 OSSL_CMP_CTX_snprint_PKIStatus          4970	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_HDR_get0_transactionID         4971	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_HDR_get0_recipNonce            4972	3_0_0	EXIST::FUNCTION:CMP
+X509_LOOKUP_store                       4973	3_0_0	EXIST::FUNCTION:
+X509_STORE_load_file                    4974	3_0_0	EXIST::FUNCTION:
+X509_STORE_load_path                    4975	3_0_0	EXIST::FUNCTION:
+X509_STORE_load_store                   4976	3_0_0	EXIST::FUNCTION:
diff --git a/util/libssl.num b/util/libssl.num
index 25e12ab36a..c643acbb24 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -354,7 +354,7 @@ SSL_set_session_id_context              354	3_0_0	EXIST::FUNCTION:
 SSL_new                                 355	3_0_0	EXIST::FUNCTION:
 TLSv1_1_method                          356	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_1_1_0,TLS1_1_METHOD
 SSL_CTX_get_cert_store                  357	3_0_0	EXIST::FUNCTION:
-SSL_CTX_load_verify_locations           358	3_0_0	EXIST::FUNCTION:
+SSL_CTX_load_verify_locations           358	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3
 SSL_SESSION_print_fp                    359	3_0_0	EXIST::FUNCTION:STDIO
 SSL_get0_dane_tlsa                      360	3_0_0	EXIST::FUNCTION:
 SSL_CTX_set_generate_session_id         361	3_0_0	EXIST::FUNCTION:
@@ -506,3 +506,8 @@ SSL_get_async_status                    506	3_0_0	EXIST::FUNCTION:
 SSL_sendfile                            507	3_0_0	EXIST::FUNCTION:
 OSSL_default_cipher_list                508	3_0_0	EXIST::FUNCTION:
 OSSL_default_ciphersuites               509	3_0_0	EXIST::FUNCTION:
+SSL_add_store_cert_subjects_to_stack    510	3_0_0	EXIST::FUNCTION:
+SSL_CTX_set_default_verify_store        511	3_0_0	EXIST::FUNCTION:
+SSL_CTX_load_verify_file                512	3_0_0	EXIST::FUNCTION:
+SSL_CTX_load_verify_dir                 513	3_0_0	EXIST::FUNCTION:
+SSL_CTX_load_verify_store               514	3_0_0	EXIST::FUNCTION:
diff --git a/util/missingmacro.txt b/util/missingmacro.txt
index 86142892a6..d42a26a6a2 100644
--- a/util/missingmacro.txt
+++ b/util/missingmacro.txt
@@ -194,7 +194,9 @@ X509_extract_key
 X509_REQ_extract_key
 X509_name_cmp
 X509_LOOKUP_load_file
+X509_LOOKUP_load_store
 X509_LOOKUP_add_dir
+X509_LOOKUP_add_store
 X509V3_conf_err
 X509V3_set_ctx_test
 X509V3_set_ctx_nodb
diff --git a/util/missingssl.txt b/util/missingssl.txt
index 3ee475d87a..be1e5f87b9 100644
--- a/util/missingssl.txt
+++ b/util/missingssl.txt
@@ -19,6 +19,7 @@ SSL_SRP_CTX_free
 SSL_SRP_CTX_init
 SSL_add_dir_cert_subjects_to_stack
 SSL_add_file_cert_subjects_to_stack
+SSL_add_store_cert_subjects_to_stack
 SSL_add_ssl_module
 SSL_certs_clear
 SSL_copy_session_id


More information about the openssl-commits mailing list