summaryrefslogtreecommitdiffstats
path: root/engines
diff options
context:
space:
mode:
authorRichard Levitte <levitte@openssl.org>2020-07-23 16:56:59 +0200
committerRichard Levitte <levitte@openssl.org>2020-09-03 17:48:32 +0200
commit16feca71544681cabf873fecd3f860f9853bdf07 (patch)
treea1dce6397911d95de73f10208b65dbba04526ac6 /engines
parentbd7a6f16eb52c5c022b2555810efd99006db0a02 (diff)
STORE: Move the built-in 'file:' loader to become an engine module
From this point on, this engine must be specifically specified. To replace the internal EMBEDDED hack with something unique for the new module, functions to create application specific OSSL_STORE_INFO types were added. Furthermore, the following function had to be exported: ossl_do_blob_header() ossl_do_PVK_header() asn1_d2i_read_bio() Finally, evp_pkcs82pkey_int() has become public under a new name, EVP_PKCS82PKEY_with_libctx() Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/12587)
Diffstat (limited to 'engines')
-rw-r--r--engines/build.info10
-rw-r--r--engines/e_loader_attic.c1790
-rw-r--r--engines/e_loader_attic.ec3
-rw-r--r--engines/e_loader_attic.txt23
-rw-r--r--engines/e_loader_attic_err.c73
-rw-r--r--engines/e_loader_attic_err.h43
6 files changed, 1941 insertions, 1 deletions
diff --git a/engines/build.info b/engines/build.info
index 3bfe1dc057..4e83dbf9bc 100644
--- a/engines/build.info
+++ b/engines/build.info
@@ -70,7 +70,7 @@ IF[{- !$disabled{"engine"} -}]
ENDIF
ENDIF
- MODULES{noinst,engine}=ossltest dasync
+ MODULES{noinst,engine}=ossltest dasync loader_attic
SOURCE[dasync]=e_dasync.c
DEPEND[dasync]=../libcrypto
INCLUDE[dasync]=../include
@@ -86,6 +86,14 @@ IF[{- !$disabled{"engine"} -}]
SOURCE[ossltest]=ossltest.ld
GENERATE[ossltest.ld]=../util/engines.num
ENDIF
+
+ SOURCE[loader_attic]=e_loader_attic.c
+ DEPEND[loader_attic]=../libcrypto
+ INCLUDE[loader_attic]=../include
+ IF[{- defined $target{shared_defflag} -}]
+ SOURCE[loader_attic]=loader_attic.ld
+ GENERATE[loader_attic.ld]=../util/engines.num
+ ENDIF
ENDIF
GENERATE[e_padlock-x86.s]=asm/e_padlock-x86.pl
GENERATE[e_padlock-x86_64.s]=asm/e_padlock-x86_64.pl
diff --git a/engines/e_loader_attic.c b/engines/e_loader_attic.c
new file mode 100644
index 0000000000..581bfb0285
--- /dev/null
+++ b/engines/e_loader_attic.c
@@ -0,0 +1,1790 @@
+/*
+ * Copyright 2016-2020 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
+ */
+
+/* THIS ENGINE IS FOR TESTING PURPOSES ONLY. */
+
+/* We need to use some engine deprecated APIs */
+#define OPENSSL_SUPPRESS_DEPRECATED
+
+/* #include "e_os.h" */
+#include <string.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <openssl/bio.h>
+#include <openssl/dsa.h> /* For d2i_DSAPrivateKey */
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h> /* For the PKCS8 stuff o.O */
+#include <openssl/rsa.h> /* For d2i_RSAPrivateKey */
+#include <openssl/safestack.h>
+#include <openssl/store.h>
+#include <openssl/ui.h>
+#include <openssl/engine.h>
+#include <openssl/x509.h> /* For the PKCS8 stuff o.O */
+#include "internal/asn1.h" /* For asn1_d2i_read_bio */
+#include "internal/pem.h" /* For PVK and "blob" PEM headers */
+#include "internal/o_dir.h"
+#include "internal/cryptlib.h"
+
+#include "e_loader_attic_err.c"
+
+DEFINE_STACK_OF(X509)
+DEFINE_STACK_OF(OSSL_STORE_INFO)
+
+#ifdef _WIN32
+# define stat _stat
+# define strncasecmp _strnicmp
+#endif
+
+#ifndef S_ISDIR
+# define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)
+#endif
+
+/*-
+ * Password prompting
+ * ------------------
+ */
+
+static char *file_get_pass(const UI_METHOD *ui_method, char *pass,
+ size_t maxsize, const char *desc, const char *info,
+ void *data)
+{
+ UI *ui = UI_new();
+ char *prompt = NULL;
+
+ if (ui == NULL) {
+ ATTICerr(0, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+
+ if (ui_method != NULL)
+ UI_set_method(ui, ui_method);
+ UI_add_user_data(ui, data);
+
+ if ((prompt = UI_construct_prompt(ui, desc, info)) == NULL) {
+ ATTICerr(0, ERR_R_MALLOC_FAILURE);
+ pass = NULL;
+ } else if (!UI_add_input_string(ui, prompt, UI_INPUT_FLAG_DEFAULT_PWD,
+ pass, 0, maxsize - 1)) {
+ ATTICerr(0, ERR_R_UI_LIB);
+ pass = NULL;
+ } else {
+ switch (UI_process(ui)) {
+ case -2:
+ ATTICerr(0, ATTIC_R_UI_PROCESS_INTERRUPTED_OR_CANCELLED);
+ pass = NULL;
+ break;
+ case -1:
+ ATTICerr(0, ERR_R_UI_LIB);
+ pass = NULL;
+ break;
+ default:
+ break;
+ }
+ }
+
+ OPENSSL_free(prompt);
+ UI_free(ui);
+ return pass;
+}
+
+struct pem_pass_data {
+ const UI_METHOD *ui_method;
+ void *data;
+ const char *prompt_desc;
+ const char *prompt_info;
+};
+
+static int file_fill_pem_pass_data(struct pem_pass_data *pass_data,
+ const char *desc, const char *info,
+ const UI_METHOD *ui_method, void *ui_data)
+{
+ if (pass_data == NULL)
+ return 0;
+ pass_data->ui_method = ui_method;
+ pass_data->data = ui_data;
+ pass_data->prompt_desc = desc;
+ pass_data->prompt_info = info;
+ return 1;
+}
+
+/* This is used anywhere a pem_password_cb is needed */
+static int file_get_pem_pass(char *buf, int num, int w, void *data)
+{
+ struct pem_pass_data *pass_data = data;
+ char *pass = file_get_pass(pass_data->ui_method, buf, num,
+ pass_data->prompt_desc, pass_data->prompt_info,
+ pass_data->data);
+
+ return pass == NULL ? 0 : strlen(pass);
+}
+
+/*
+ * Check if |str| ends with |suffix| preceded by a space, and if it does,
+ * return the index of that space. If there is no such suffix in |str|,
+ * return -1.
+ * For |str| == "FOO BAR" and |suffix| == "BAR", the returned value is 3.
+ */
+static int check_suffix(const char *str, const char *suffix)
+{
+ int str_len = strlen(str);
+ int suffix_len = strlen(suffix) + 1;
+ const char *p = NULL;
+
+ if (suffix_len >= str_len)
+ return -1;
+ p = str + str_len - suffix_len;
+ if (*p != ' '
+ || strcmp(p + 1, suffix) != 0)
+ return -1;
+ return p - str;
+}
+
+/*
+ * EMBEDDED is a special type of OSSL_STORE_INFO, specially for the file
+ * handlers, so we define it internally. This uses the possibility to
+ * create an OSSL_STORE_INFO with a generic data pointer and arbitrary
+ * type number.
+ *
+ * This is used by a FILE_HANDLER's try_decode function to signal that it
+ * has decoded the incoming blob into a new blob, and that the attempted
+ * decoding should be immediately restarted with the new blob, using the
+ * new PEM name.
+ */
+/* Negative numbers are never used for public OSSL_STORE_INFO types */
+#define STORE_INFO_EMBEDDED -1
+
+/* This is the embedded data */
+struct embedded_st {
+ BUF_MEM *blob;
+ char *pem_name;
+};
+
+/* Helper functions */
+static struct embedded_st *get0_EMBEDDED(OSSL_STORE_INFO *info)
+{
+ return OSSL_STORE_INFO_get0_data(STORE_INFO_EMBEDDED, info);
+}
+
+static void store_info_free(OSSL_STORE_INFO *info)
+{
+ struct embedded_st *data;
+
+ if (info != NULL && (data = get0_EMBEDDED(info)) != NULL) {
+ BUF_MEM_free(data->blob);
+ OPENSSL_free(data->pem_name);
+ OPENSSL_free(data);
+ }
+ OSSL_STORE_INFO_free(info);
+}
+
+static OSSL_STORE_INFO *new_EMBEDDED(const char *new_pem_name,
+ BUF_MEM *embedded)
+{
+ OSSL_STORE_INFO *info = NULL;
+ struct embedded_st *data = NULL;
+
+ if ((data = OPENSSL_zalloc(sizeof(*data))) == NULL
+ || (info = OSSL_STORE_INFO_new(STORE_INFO_EMBEDDED, data)) == NULL) {
+ ATTICerr(0, ERR_R_MALLOC_FAILURE);
+ OPENSSL_free(data);
+ return NULL;
+ }
+
+ data->pem_name =
+ new_pem_name == NULL ? NULL : OPENSSL_strdup(new_pem_name);
+
+ if (new_pem_name != NULL && data->pem_name == NULL) {
+ ATTICerr(0, ERR_R_MALLOC_FAILURE);
+ store_info_free(info);
+ info = NULL;
+ }
+ data->blob = embedded;
+
+ return info;
+}
+
+/*-
+ * The file scheme decoders
+ * ------------------------
+ *
+ * Each possible data type has its own decoder, which either operates
+ * through a given PEM name, or attempts to decode to see if the blob
+ * it's given is decodable for its data type. The assumption is that
+ * only the correct data type will match the content.
+ */
+
+/*-
+ * The try_decode function is called to check if the blob of data can
+ * be used by this handler, and if it can, decodes it into a supported
+ * OpenSSL type and returns a OSSL_STORE_INFO with the decoded data.
+ * Input:
+ * pem_name: If this blob comes from a PEM file, this holds
+ * the PEM name. If it comes from another type of
+ * file, this is NULL.
+ * pem_header: If this blob comes from a PEM file, this holds
+ * the PEM headers. If it comes from another type of
+ * file, this is NULL.
+ * blob: The blob of data to match with what this handler
+ * can use.
+ * len: The length of the blob.
+ * handler_ctx: For a handler marked repeatable, this pointer can
+ * be used to create a context for the handler. IT IS
+ * THE HANDLER'S RESPONSIBILITY TO CREATE AND DESTROY
+ * THIS CONTEXT APPROPRIATELY, i.e. create on first call
+ * and destroy when about to return NULL.
+ * matchcount: A pointer to an int to count matches for this data.
+ * Usually becomes 0 (no match) or 1 (match!), but may
+ * be higher in the (unlikely) event that the data matches
+ * more than one possibility. The int will always be
+ * zero when the function is called.
+ * ui_method: Application UI method for getting a password, pin
+ * or any other interactive data.
+ * ui_data: Application data to be passed to ui_method when
+ * it's called.
+ * libctx: The library context to be used if applicable
+ * propq: The property query string for any algorithm fetches
+ * Output:
+ * a OSSL_STORE_INFO
+ */
+typedef OSSL_STORE_INFO *(*file_try_decode_fn)(const char *pem_name,
+ const char *pem_header,
+ const unsigned char *blob,
+ size_t len, void **handler_ctx,
+ int *matchcount,
+ const UI_METHOD *ui_method,
+ void *ui_data, const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq);
+/*
+ * The eof function should return 1 if there's no more data to be found
+ * with the handler_ctx, otherwise 0. This is only used when the handler is
+ * marked repeatable.
+ */
+typedef int (*file_eof_fn)(void *handler_ctx);
+/*
+ * The destroy_ctx function is used to destroy the handler_ctx that was
+ * initiated by a repeatable try_decode function. This is only used when
+ * the handler is marked repeatable.
+ */
+typedef void (*file_destroy_ctx_fn)(void **handler_ctx);
+
+typedef struct file_handler_st {
+ const char *name;
+ file_try_decode_fn try_decode;
+ file_eof_fn eof;
+ file_destroy_ctx_fn destroy_ctx;
+
+ /* flags */
+ int repeatable;
+} FILE_HANDLER;
+
+/*
+ * PKCS#12 decoder. It operates by decoding all of the blob content,
+ * extracting all the interesting data from it and storing them internally,
+ * then serving them one piece at a time.
+ */
+static OSSL_STORE_INFO *try_decode_PKCS12(const char *pem_name,
+ const char *pem_header,
+ const unsigned char *blob,
+ size_t len, void **pctx,
+ int *matchcount,
+ const UI_METHOD *ui_method,
+ void *ui_data, const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
+{
+ OSSL_STORE_INFO *store_info = NULL;
+ STACK_OF(OSSL_STORE_INFO) *ctx = *pctx;
+
+ if (ctx == NULL) {
+ /* Initial parsing */
+ PKCS12 *p12;
+
+ if (pem_name != NULL)
+ /* No match, there is no PEM PKCS12 tag */
+ return NULL;
+
+ if ((p12 = d2i_PKCS12(NULL, &blob, len)) != NULL) {
+ char *pass = NULL;
+ char tpass[PEM_BUFSIZE];
+ EVP_PKEY *pkey = NULL;
+ X509 *cert = NULL;
+ STACK_OF(X509) *chain = NULL;
+
+ *matchcount = 1;
+
+ if (PKCS12_verify_mac(p12, "", 0)
+ || PKCS12_verify_mac(p12, NULL, 0)) {
+ pass = "";
+ } else {
+ if ((pass = file_get_pass(ui_method, tpass, PEM_BUFSIZE,
+ "PKCS12 import pass phrase", uri,
+ ui_data)) == NULL) {
+ ATTICerr(0, ATTIC_R_PASSPHRASE_CALLBACK_ERROR);
+ goto p12_end;
+ }
+ if (!PKCS12_verify_mac(p12, pass, strlen(pass))) {
+ ATTICerr(0, ATTIC_R_ERROR_VERIFYING_PKCS12_MAC);
+ goto p12_end;
+ }
+ }
+
+ if (PKCS12_parse(p12, pass, &pkey, &cert, &chain)) {
+ OSSL_STORE_INFO *osi_pkey = NULL;
+ OSSL_STORE_INFO *osi_cert = NULL;
+ OSSL_STORE_INFO *osi_ca = NULL;
+ int ok = 1;
+
+ if ((ctx = sk_OSSL_STORE_INFO_new_null()) != NULL) {
+ if (pkey != NULL) {
+ if ((osi_pkey = OSSL_STORE_INFO_new_PKEY(pkey)) != NULL
+ /* clearing pkey here avoids case distinctions */
+ && (pkey = NULL) == NULL
+ && sk_OSSL_STORE_INFO_push(ctx, osi_pkey) != 0)
+ osi_pkey = NULL;
+ else
+ ok = 0;
+ }
+ if (ok && cert != NULL) {
+ if ((osi_cert = OSSL_STORE_INFO_new_CERT(cert)) != NULL
+ /* clearing cert here avoids case distinctions */
+ && (cert = NULL) == NULL
+ && sk_OSSL_STORE_INFO_push(ctx, osi_cert) != 0)
+ osi_cert = NULL;
+ else
+ ok = 0;
+ }
+ while (ok && sk_X509_num(chain) > 0) {
+ X509 *ca = sk_X509_value(chain, 0);
+
+ if ((osi_ca = OSSL_STORE_INFO_new_CERT(ca)) != NULL
+ && sk_X509_shift(chain) != NULL
+ && sk_OSSL_STORE_INFO_push(ctx, osi_ca) != 0)
+ osi_ca = NULL;
+ else
+ ok = 0;
+ }
+ }
+ EVP_PKEY_free(pkey);
+ X509_free(cert);
+ sk_X509_pop_free(chain, X509_free);
+ store_info_free(osi_pkey);
+ store_info_free(osi_cert);
+ store_info_free(osi_ca);
+ if (!ok) {
+ sk_OSSL_STORE_INFO_pop_free(ctx, store_info_free);
+ ctx = NULL;
+ }
+ *pctx = ctx;
+ }
+ }
+ p12_end:
+ PKCS12_free(p12);
+ if (ctx == NULL)
+ return NULL;
+ }
+
+ *matchcount = 1;
+ store_info = sk_OSSL_STORE_INFO_shift(ctx);
+ return store_info;
+}
+
+static int eof_PKCS12(void *ctx_)
+{
+ STACK_OF(OSSL_STORE_INFO) *ctx = ctx_;
+
+ return ctx == NULL || sk_OSSL_STORE_INFO_num(ctx) == 0;
+}
+
+static void destroy_ctx_PKCS12(void **pctx)
+{
+ STACK_OF(OSSL_STORE_INFO) *ctx = *pctx;
+
+ sk_OSSL_STORE_INFO_pop_free(ctx, store_info_free);
+ *pctx = NULL;
+}
+
+static FILE_HANDLER PKCS12_handler = {
+ "PKCS12",
+ try_decode_PKCS12,
+ eof_PKCS12,
+ destroy_ctx_PKCS12,
+ 1 /* repeatable */
+};
+
+/*
+ * Encrypted PKCS#8 decoder. It operates by just decrypting the given blob
+ * into a new blob, which is returned as an EMBEDDED STORE_INFO. The whole
+ * decoding process will then start over with the new blob.
+ */
+static OSSL_STORE_INFO *try_decode_PKCS8Encrypted(const char *pem_name,
+ const char *pem_header,
+ const unsigned char *blob,
+ size_t len, void **pctx,
+ int *matchcount,
+ const UI_METHOD *ui_method,
+ void *ui_data,
+ const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
+{
+ X509_SIG *p8 = NULL;
+ char kbuf[PEM_BUFSIZE];
+ char *pass = NULL;
+ const X509_ALGOR *dalg = NULL;
+ const ASN1_OCTET_STRING *doct = NULL;
+ OSSL_STORE_INFO *store_info = NULL;
+ BUF_MEM *mem = NULL;
+ unsigned char *new_data = NULL;
+ int new_data_len;
+
+ if (pem_name != NULL) {
+ if (strcmp(pem_name, PEM_STRING_PKCS8) != 0)
+ return NULL;
+ *matchcount = 1;
+ }
+
+ if ((p8 = d2i_X509_SIG(NULL, &blob, len)) == NULL)
+ return NULL;
+
+ *matchcount = 1;
+
+ if ((mem = BUF_MEM_new()) == NULL) {
+ ATTICerr(0, ERR_R_MALLOC_FAILURE);
+ goto nop8;
+ }
+
+ if ((pass = file_get_pass(ui_method, kbuf, PEM_BUFSIZE,
+ "PKCS8 decrypt pass phrase", uri,
+ ui_data)) == NULL) {
+ ATTICerr(0, ATTIC_R_BAD_PASSWORD_READ);
+ goto nop8;
+ }
+
+ X509_SIG_get0(p8, &dalg, &doct);
+ if (!PKCS12_pbe_crypt(dalg, pass, strlen(pass), doct->data, doct->length,
+ &new_data, &new_data_len, 0))
+ goto nop8;
+
+ mem->data = (char *)new_data;
+ mem->max = mem->length = (size_t)new_data_len;
+ X509_SIG_free(p8);
+
+ store_info = new_EMBEDDED(PEM_STRING_PKCS8INF, mem);
+ if (store_info == NULL) {
+ ATTICerr(0, ERR_R_MALLOC_FAILURE);
+ goto nop8;
+ }
+
+ return store_info;
+ nop8:
+ X509_SIG_free(p8);
+ BUF_MEM_free(mem);
+ return NULL;
+}
+
+static FILE_HANDLER PKCS8Encrypted_handler = {
+ "PKCS8Encrypted",
+ try_decode_PKCS8Encrypted
+};
+
+/*
+ * Private key decoder. Decodes all sorts of private keys, both PKCS#8
+ * encoded ones and old style PEM ones (with the key type is encoded into
+ * the PEM name).
+ */
+static OSSL_STORE_INFO *try_decode_PrivateKey(const char *pem_name,
+ const char *pem_header,
+ const unsigned char *blob,
+ size_t len, void **pctx,
+ int *matchcount,
+ const UI_METHOD *ui_method,
+ void *ui_data, const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
+{
+ OSSL_STORE_INFO *store_info = NULL;
+ EVP_PKEY *pkey = NULL;
+ const EVP_PKEY_ASN1_METHOD *ameth = NULL;
+
+ if (pem_name != NULL) {
+ if (strcmp(pem_name, PEM_STRING_PKCS8INF) == 0) {
+ PKCS8_PRIV_KEY_INFO *p8inf =
+ d2i_PKCS8_PRIV_KEY_INFO(NULL, &blob, len);
+
+ *matchcount = 1;
+ if (p8inf != NULL)
+ pkey = EVP_PKCS82PKEY_with_libctx(p8inf, libctx, propq);
+ PKCS8_PRIV_KEY_INFO_free(p8inf);
+ } else {
+ int slen;
+ int pkey_id;
+
+ if ((slen = check_suffix(pem_name, "PRIVATE KEY")) > 0
+ && (ameth = EVP_PKEY_asn1_find_str(NULL, pem_name,
+ slen)) != NULL
+ && EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL,
+ ameth)) {
+ *matchcount = 1;
+ pkey = d2i_PrivateKey_ex(pkey_id, NULL, &blob, len,
+ libctx, propq);
+ }
+ }
+ } else {
+ int i;
+#ifndef OPENSSL_NO_ENGINE
+ ENGINE *curengine = ENGINE_get_first();
+
+ while (curengine != NULL) {
+ ENGINE_PKEY_ASN1_METHS_PTR asn1meths =
+ ENGINE_get_pkey_asn1_meths(curengine);
+
+ if (asn1meths != NULL) {
+ const int *nids = NULL;
+ int nids_n = asn1meths(curengine, NULL, &nids, 0);
+
+ for (i = 0; i < nids_n; i++) {
+ EVP_PKEY_ASN1_METHOD *ameth2 = NULL;
+ EVP_PKEY *tmp_pkey = NULL;
+ const unsigned char *tmp_blob = blob;
+ int pkey_id, pkey_flags;
+
+ if (!asn1meths(curengine, &ameth2, NULL, nids[i])
+ || !EVP_PKEY_asn1_get0_info(&pkey_id, NULL,
+ &pkey_flags, NULL, NULL,
+ ameth2)
+ || (pkey_flags & ASN1_PKEY_ALIAS) != 0)
+ continue;
+
+ ERR_set_mark(); /* prevent flooding error queue */
+ tmp_pkey = d2i_PrivateKey_ex(pkey_id, NULL,
+ &tmp_blob, len,
+ libctx, propq);
+ if (tmp_pkey != NULL) {
+ if (pkey != NULL)
+ EVP_PKEY_free(tmp_pkey);
+ else
+ pkey = tmp_pkey;
+ (*matchcount)++;
+ }
+ ERR_pop_to_mark();
+ }
+ }
+ curengine = ENGINE_get_next(curengine);
+ }
+#endif
+
+ for (i = 0; i < EVP_PKEY_asn1_get_count(); i++) {
+ EVP_PKEY *tmp_pkey = NULL;
+ const unsigned char *tmp_blob = blob;
+ int pkey_id, pkey_flags;
+
+ ameth = EVP_PKEY_asn1_get0(i);
+ if (!EVP_PKEY_asn1_get0_info(&pkey_id, NULL, &pkey_flags, NULL,
+ NULL, ameth)
+ || (pkey_flags & ASN1_PKEY_ALIAS) != 0)
+ continue;
+
+ ERR_set_mark(); /* prevent flooding error queue */
+ tmp_pkey = d2i_PrivateKey_ex(pkey_id, NULL, &tmp_blob, len,
+ libctx, propq);
+ if (tmp_pkey != NULL) {
+ if (pkey != NULL)
+ EVP_PKEY_free(tmp_pkey);
+ else
+ pkey = tmp_pkey;
+ (*matchcount)++;
+ }
+ ERR_pop_to_mark();
+ }
+
+ if (*matchcount > 1) {
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ }
+ }
+ if (pkey == NULL)
+ /* No match */
+ return NULL;
+
+ store_info = OSSL_STORE_INFO_new_PKEY(pkey);
+ if (store_info == NULL)
+ EVP_PKEY_free(pkey);
+
+ return store_info;
+}
+
+static FILE_HANDLER PrivateKey_handler = {
+ "PrivateKey",
+ try_decode_PrivateKey
+};
+
+/*
+ * Public key decoder. Only supports SubjectPublicKeyInfo formatted keys.
+ */
+static OSSL_STORE_INFO *try_decode_PUBKEY(const char *pem_name,
+ const char *pem_header,
+ const unsigned char *blob,
+ size_t len, void **pctx,
+ int *matchcount,
+ const UI_METHOD *ui_method,
+ void *ui_data, const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
+{
+ OSSL_STORE_INFO *store_info = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ if (pem_name != NULL) {
+ if (strcmp(pem_name, PEM_STRING_PUBLIC) != 0)
+ /* No match */
+ return NULL;
+ *matchcount = 1;
+ }
+
+ if ((pkey = d2i_PUBKEY(NULL, &blob, len)) != NULL) {
+ *matchcount = 1;
+ store_info = OSSL_STORE_INFO_new_PUBKEY(pkey);
+ }
+
+ return store_info;
+}
+
+static FILE_HANDLER PUBKEY_handler = {
+ "PUBKEY",
+ try_decode_PUBKEY
+};
+
+/*
+ * Key parameter decoder.
+ */
+static OSSL_STORE_INFO *try_decode_params(const char *pem_name,
+ const char *pem_header,
+ const unsigned char *blob,
+ size_t len, void **pctx,
+ int *matchcount,
+ const UI_METHOD *ui_method,
+ void *ui_data, const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
+{
+ OSSL_STORE_INFO *store_info = NULL;
+ EVP_PKEY *pkey = NULL;
+ const EVP_PKEY_ASN1_METHOD *ameth = NULL;
+
+ if (pem_name != NULL) {
+ int slen;
+ int pkey_id;
+
+ if ((slen = check_suffix(pem_name, "PARAMETERS")) > 0
+ && (ameth = EVP_PKEY_asn1_find_str(NULL, pem_name, slen)) != NULL
+ && EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL,
+ ameth)) {
+ *matchcount = 1;
+ pkey = d2i_KeyParams(pkey_id, NULL, &blob, len);
+ }
+ } else {
+ int i;
+
+ for (i = 0; i < EVP_PKEY_asn1_get_count(); i++) {
+ EVP_PKEY *tmp_pkey = NULL;
+ const unsigned char *tmp_blob = blob;
+ int pkey_id, pkey_flags;
+
+ ameth = EVP_PKEY_asn1_get0(i);
+ if (!EVP_PKEY_asn1_get0_info(&pkey_id, NULL, &pkey_flags, NULL,
+ NULL, ameth)
+ || (pkey_flags & ASN1_PKEY_ALIAS) != 0)
+ continue;
+
+ ERR_set_mark(); /* prevent flooding error queue */
+
+ tmp_pkey = d2i_KeyParams(pkey_id, NULL, &tmp_blob, len);
+
+ if (tmp_pkey != NULL) {
+ if (pkey != NULL)
+ EVP_PKEY_free(tmp_pkey);
+ else
+ pkey = tmp_pkey;
+ (*matchcount)++;
+ }
+ ERR_pop_to_mark();
+ }
+
+ if (*matchcount > 1) {
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ }
+ }
+ if (pkey == NULL)
+ /* No match */
+ return NULL;
+
+ store_info = OSSL_STORE_INFO_new_PARAMS(pkey);
+ if (store_info == NULL)
+ EVP_PKEY_free(pkey);
+
+ return store_info;
+}
+
+static FILE_HANDLER params_handler = {
+ "params",
+ try_decode_params
+};
+
+/*
+ * X.509 certificate decoder.
+ */
+static OSSL_STORE_INFO *try_decode_X509Certificate(const char *pem_name,
+ const char *pem_header,
+ const unsigned char *blob,
+ size_t len, void **pctx,
+ int *matchcount,
+ const UI_METHOD *ui_method,
+ void *ui_data,
+ const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
+{
+ OSSL_STORE_INFO *store_info = NULL;
+ X509 *cert = NULL;
+
+ /*
+ * In most cases, we can try to interpret the serialized data as a trusted
+ * cert (X509 + X509_AUX) and fall back to reading it as a normal cert
+ * (just X509), but if the PEM name specifically declares it as a trusted
+ * cert, then no fallback should be engaged. |ignore_trusted| tells if
+ * the fallback can be used (1) or not (0).
+ */
+ int ignore_trusted = 1;
+
+ if (pem_name != NULL) {
+ if (strcmp(pem_name, PEM_STRING_X509_TRUSTED) == 0)
+ ignore_trusted = 0;
+ else if (strcmp(pem_name, PEM_STRING_X509_OLD) != 0
+ && strcmp(pem_name, PEM_STRING_X509) != 0)
+ /* No match */
+ return NULL;
+ *matchcount = 1;
+ }
+
+ cert = X509_new_with_libctx(libctx, propq);
+ if (cert == NULL)
+ return NULL;
+
+ if ((d2i_X509_AUX(&cert, &blob, len)) != NULL
+ || (ignore_trusted && (d2i_X509(&cert, &blob, len)) != NULL)) {
+ *matchcount = 1;
+ store_info = OSSL_STORE_INFO_new_CERT(cert);
+ }
+
+ if (store_info == NULL)
+ X509_free(cert);
+
+ return store_info;
+}
+
+static FILE_HANDLER X509Certificate_handler = {
+ "X509Certificate",
+ try_decode_X509Certificate
+};
+
+/*
+ * X.509 CRL decoder.
+ */
+static OSSL_STORE_INFO *try_decode_X509CRL(const char *pem_name,
+ const char *pem_header,
+ const unsigned char *blob,
+ size_t len, void **pctx,
+ int *matchcount,
+ const UI_METHOD *ui_method,
+ void *ui_data, const char *uri,
+ OPENSSL_CTX *libctx,
+ const char *propq)
+{
+ OSSL_STORE_INFO *store_info = NULL;
+ X509_CRL *crl = NULL;
+
+ if (pem_name != NULL) {
+ if (strcmp(pem_name, PEM_STRING_X509_CRL) != 0)
+ /* No match */
+ return NULL;
+ *matchcount = 1;
+ }
+
+ if ((crl = d2i_X509_CRL(NULL, &blob, len)) != NULL) {
+ *matchcount = 1;
+ store_info = OSSL_STORE_INFO_new_CRL(crl);
+ }
+
+ if (store_info == NULL)
+ X509_CRL_free(crl);
+
+ return store_info;
+}
+
+static FILE_HANDLER X509CRL_handler = {
+ "X509CRL",
+ try_decode_X509CRL
+};
+
+/*
+ * To finish it all off, we collect all the handlers.
+ */
+static const FILE_HANDLER *file_handlers[] = {
+ &PKCS12_handler,
+ &PKCS8Encrypted_handler,
+ &X509Certificate_handler,
+ &X509CRL_handler,
+ &params_handler,
+ &PUBKEY_handler,
+ &PrivateKey_handler,
+};
+
+
+/*-
+ * The loader itself
+ * -----------------
+ */
+
+struct ossl_store_loader_ctx_st {
+ char *uri; /* The URI we currently try to load */
+ enum {
+ is_raw = 0,
+ is_pem,
+ is_dir
+ } type;
+ int errcnt;
+#define FILE_FLAG_SECMEM (1<<0)
+#define FILE_FLAG_ATTACHED (1<<1)
+ unsigned int flags;
+ union {
+ struct { /* Used with is_raw and is_pem */
+ BIO *file;
+
+ /*
+ * The following are used when the handler is marked as
+ * repeatable
+ */
+ const FILE_HANDLER *last_handler;
+ void *last_handler_ctx;
+ } file;
+ struct { /* Used with is_dir */
+ OPENSSL_DIR_CTX *ctx;
+ int end_reached;
+
+ /*
+ * When a search expression is given, these are filled in.
+ * |search_name| contains the file basename to look for.
+ * The string is exactly 8 characters long.
+ */
+ char search_name[9];
+
+ /*
+ * The directory reading utility we have combines opening with
+ * reading the first name. To make sure we can detect the end
+ * at the right time, we read early and cache the name.
+ */
+ const char *last_entry;
+ int last_errno;
+ } dir;
+ } _;
+
+ /* Expected object type. May be unspecified */
+ int expected_type;
+
+ OPENSSL_CTX *libctx;
+ char *propq;
+};
+
+static void OSSL_STORE_LOADER_CTX_free(OSSL_STORE_LOADER_CTX *ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ OPENSSL_free(ctx->propq);
+ OPENSSL_free(ctx->uri);
+ if (ctx->type != is_dir) {
+ if (ctx->_.file.last_handler != NULL) {
+ ctx->_.file.last_handler->destroy_ctx(&ctx->_.file.last_handler_ctx);
+ ctx->_.file.last_handler_ctx = NULL;
+ ctx->_.file.last_handler = NULL;
+ }
+ }
+ OPENSSL_free(ctx);
+}
+
+static int file_find_type(OSSL_STORE_LOADER_CTX *ctx)
+{
+ BIO *buff = NULL;
+ char peekbuf[4096] = { 0, };
+
+ if ((buff = BIO_new(BIO_f_buffer())) == NULL)
+ return 0;
+
+ ctx->_.file.file = BIO_push(buff, ctx->_.file.file);
+ if (BIO_buffer_peek(ctx->_.file.file, peekbuf, sizeof(peekbuf) - 1) > 0) {
+