/*
* Copyright 2020-2021 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 "e_os.h"
#include <string.h>
#include <openssl/core.h>
#include <openssl/core_names.h>
#include <openssl/core_object.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>
#include <openssl/provider.h>
#include <openssl/decoder.h>
#include <openssl/store.h>
#include "internal/provider.h"
#include "internal/passphrase.h"
#include "crypto/evp.h"
#include "crypto/x509.h"
#include "store_local.h"
#ifndef OSSL_OBJECT_PKCS12
/*
* The object abstraction doesn't know PKCS#12, but we want to indicate
* it anyway, so we create our own. Since the public macros use positive
* numbers, negative ones should be fine. They must never slip out from
* this translation unit anyway.
*/
# define OSSL_OBJECT_PKCS12 -1
#endif
/*
* ossl_store_handle_load_result() is initially written to be a companion
* to our 'file:' scheme provider implementation, but has been made generic
* to serve others as well.
*
* This result handler takes any object abstraction (see provider-object(7))
* and does the best it can with it. If the object is passed by value (not
* by reference), the contents are currently expected to be DER encoded.
* If an object type is specified, that will be respected; otherwise, this
* handler will guess the contents, by trying the following in order:
*
* 1. Decode it into an EVP_PKEY, using OSSL_DECODER.
* 2. Decode it into an X.509 certificate, using d2i_X509 / d2i_X509_AUX.
* 3. Decode it into an X.509 CRL, using d2i_X509_CRL.
* 4. Decode it into a PKCS#12 structure, using d2i_PKCS12 (*).
*
* For the 'file:' scheme implementation, this is division of labor. Since
* the libcrypto <-> provider interface currently doesn't support certain
* structures as first class objects, they must be unpacked from DER here
* rather than in the provider. The current exception is asymmetric keys,
* which can reside within the provider boundary, most of all thanks to
* OSSL_FUNC_keymgmt_load(), which allows loading the key material by
* reference.
*/
struct extracted_param_data_st {
int object_type;
const char *data_type;
const char *data_structure;
const char *utf8_data;
const void *octet_data;
size_t octet_data_size;
const void *ref;
size_t ref_size;
const char *desc;
};
static int try_name(struct extracted_param_data_st *, OSSL_STORE_INFO **);
static int try_key(struct extracted_param_data_st *, OSSL_STORE_INFO **,
OSSL_STORE_CTX *, const OSSL_PROVIDER *,
OSSL_LIB_CTX *, const char *);
static int try_cert(struct extracted_param_data_st *, OSSL_STORE_INFO **,
OSSL_LIB_CTX *, const char *);
static int try_crl(struct extracted_param_data_st *, OSSL_STORE_INFO **,
OSSL_LIB_CTX *, const char *);
static int try_pkcs12(struct extracted_param_data_st *, OSSL_STORE_INFO **,
OSSL_STORE_CTX *, OSSL_LIB_CTX *, const char *);
#define SET_ERR_MARK() ERR_set_mark()
#define CLEAR_ERR_MARK() \
do { \
int err = ERR_peek_last_error(); \
\
if (ERR_GET_LIB(err) == ERR_LIB_ASN1 \
&& (ERR_GET_REASON(err) == ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE \
|| ERR_GET_REASON(err) == ASN1_R_NO_MATCHING_CHOICE_TYPE \
|| ERR_GET_REASON(err) == ERR_R_NESTED_ASN1_ERROR)) \
ERR_pop_to_mark(); \
else \
ERR_clear_last_mark(); \
} while(0)
#define RESET_ERR_MARK() \
do { \
CLEAR_ERR_MARK(); \
SET_ERR_MARK(); \
} while(0)
int ossl_store_handle_load_result(const OSSL_PARAM params[], void *arg)
{
struct ossl_load_result_data_st *cbdata = arg;
OSSL_STORE_INFO **v = &cbdata->v;
OSSL_STORE_CTX *ctx = cbdata->ctx;
const OSSL_PROVIDER *provider =
OSSL_STORE_LOADER_provider(ctx->fetched_loader);
OSSL_LIB_CTX *libctx = ossl_provider_libctx(provider);
const char *propq = ctx->properties;
const OSSL_PARAM *p;
struct extracted_param_data_st helper_data;
memset(&helper_data, 0, sizeof(helper_data));
helper_data.object_type = OSSL_OBJECT_UNKNOWN;
if ((p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_TYPE)) != NULL
&& !OSSL_PARAM_get_int(p, &helper_data.object_type))
return 0;
p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DATA_TYPE);
if (p != NULL
&& !OSSL_PARAM_get_utf8_string_ptr(p, &helper_data.data_type))
return 0;
p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DATA);
if (p != NULL
&& !OSSL_PARAM_get_octet_string_ptr(p, &helper_data.octet_data,
&helper_data.octet_data_size)
&& !OSSL_PARAM_get_utf8_string_ptr(p, &helper_data.utf8_data))
return 0;
p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DATA_STRUCTURE);
if (p != NULL
&& !OSSL_PARAM_get_utf8_string_ptr(p, &helper_data.data_structure))
return 0;
p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_REFERENCE);
if (p != NULL && !OSSL_PARAM_get_octet_string_ptr(p, &helper_data.ref,
&helper_data.ref_size))
return 0;
p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DESC);
if (p != NULL && !OSSL_PARAM_get_utf8_string_ptr(p, &helper_data.desc))
return 0;
/*
* The helper functions return 0 on actual errors, otherwise 1, even if
* they didn't fill out |*v|.
*/
SET_ERR_MARK();
if (!try_name(&helper_data, v))
goto err;
RESET_ERR_MARK();
if (!try_key(&helper_data, v, ctx, provider, libctx, propq))
goto err;
RESET_ERR_MARK();
if (!try_cert(&helper_data, v, libctx, propq))
goto err;
RESET_ERR_MARK();
if (!try_crl(&helper_data, v, libctx, propq))
goto err;
RESET_ERR_MARK();
if (!try_pkcs12(&helper_data, v, ctx, libctx, propq))
goto err;
CLEAR_ERR_MARK();
return (*v != NULL);
err:
return 0;
}
static int try_name(struct extracted_param_data_st *data, OSSL_STORE_INFO **v)
{
if (data->object_type == OSSL_OBJECT_NAME) {
char *newname = NULL, *newdesc = NULL;
if (data->utf8_data == NULL)
return 0;
if ((newname = OPENSSL_strdup(data->utf8_data)) == NULL
|| (data->desc != NULL
&& (newdesc = OPENSSL_strdup(data->desc)) == NULL)
|| (*v = OSSL_STORE_INFO_new_NAME(newname)) == NULL) {
OPENSSL_free(newname);
OPENSSL_free(newdesc);
return 0;
}
OSSL_STORE_INFO_set0_NAME_description(*v, newdesc);
}
return 1;
}
/*
* For the rest of the object types, the provider code may not know what
* type of data it gave us, so we may need to figure that out on our own.
* Therefore, we do check for OSSL_OBJECT_UNKNOWN everywhere below, and
* only return 0 on error if the object type is known.
*/
static EVP_PKEY *try_key_ref(struct extracted_param_data_st *data,
OSSL_STORE_CTX *ctx,
const