Path: blob/main/crypto/openssl/providers/implementations/storemgmt/file_store.c
107936 views
/*1* Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89/* This file has quite some overlap with engines/e_loader_attic.c */1011#include <string.h>12#include <sys/stat.h>13#include <ctype.h> /* isdigit */14#include <assert.h>1516#include <openssl/core_dispatch.h>17#include <openssl/core_names.h>18#include <openssl/core_object.h>19#include <openssl/bio.h>20#include <openssl/err.h>21#include <openssl/params.h>22#include <openssl/decoder.h>23#include <openssl/proverr.h>24#include <openssl/store.h> /* The OSSL_STORE_INFO type numbers */25#include "internal/cryptlib.h"26#include "internal/o_dir.h"27#include "crypto/decoder.h"28#include "crypto/ctype.h" /* ossl_isdigit() */29#include "prov/implementations.h"30#include "prov/bio.h"31#include "prov/providercommon.h"32#include "file_store_local.h"3334DEFINE_STACK_OF(OSSL_STORE_INFO)3536#ifdef _WIN3237#define stat _stat38#endif3940#ifndef S_ISDIR41#define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)42#endif4344static OSSL_FUNC_store_open_fn file_open;45static OSSL_FUNC_store_attach_fn file_attach;46static OSSL_FUNC_store_settable_ctx_params_fn file_settable_ctx_params;47static OSSL_FUNC_store_set_ctx_params_fn file_set_ctx_params;48static OSSL_FUNC_store_load_fn file_load;49static OSSL_FUNC_store_eof_fn file_eof;50static OSSL_FUNC_store_close_fn file_close;5152/*53* This implementation makes full use of OSSL_DECODER, and then some.54* It uses its own internal decoder implementation that reads DER and55* passes that on to the data callback; this decoder is created with56* internal OpenSSL functions, thereby bypassing the need for a surrounding57* provider. This is ok, since this is a local decoder, not meant for58* public consumption.59* Finally, it sets up its own construct and cleanup functions.60*61* Essentially, that makes this implementation a kind of glorified decoder.62*/6364struct file_ctx_st {65void *provctx;66char *uri; /* The URI we currently try to load */67enum {68IS_FILE = 0, /* Read file and pass results */69IS_DIR /* Pass directory entry names */70} type;7172union {73/* Used with |IS_FILE| */74struct {75BIO *file;7677OSSL_DECODER_CTX *decoderctx;78char *input_type;79char *propq; /* The properties we got as a parameter */80} file;8182/* Used with |IS_DIR| */83struct {84OPENSSL_DIR_CTX *ctx;85int end_reached;8687/*88* When a search expression is given, these are filled in.89* |search_name| contains the file basename to look for.90* The string is exactly 8 characters long.91*/92char search_name[9];9394/*95* The directory reading utility we have combines opening with96* reading the first name. To make sure we can detect the end97* at the right time, we read early and cache the name.98*/99const char *last_entry;100int last_errno;101} dir;102} _;103104/* Expected object type. May be unspecified */105int expected_type;106};107108static void free_file_ctx(struct file_ctx_st *ctx)109{110if (ctx == NULL)111return;112113OPENSSL_free(ctx->uri);114if (ctx->type != IS_DIR) {115OSSL_DECODER_CTX_free(ctx->_.file.decoderctx);116OPENSSL_free(ctx->_.file.propq);117OPENSSL_free(ctx->_.file.input_type);118}119OPENSSL_free(ctx);120}121122static struct file_ctx_st *new_file_ctx(int type, const char *uri,123void *provctx)124{125struct file_ctx_st *ctx = NULL;126127if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL128&& (uri == NULL || (ctx->uri = OPENSSL_strdup(uri)) != NULL)) {129ctx->type = type;130ctx->provctx = provctx;131return ctx;132}133free_file_ctx(ctx);134return NULL;135}136137static OSSL_DECODER_CONSTRUCT file_load_construct;138static OSSL_DECODER_CLEANUP file_load_cleanup;139140/*-141* Opening / attaching streams and directories142* -------------------------------------------143*/144145/*146* Function to service both file_open() and file_attach()147*148*149*/150static struct file_ctx_st *file_open_stream(BIO *source, const char *uri,151void *provctx)152{153struct file_ctx_st *ctx;154155if ((ctx = new_file_ctx(IS_FILE, uri, provctx)) == NULL) {156ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB);157goto err;158}159160ctx->_.file.file = source;161162return ctx;163err:164free_file_ctx(ctx);165return NULL;166}167168static void *file_open_dir(const char *path, const char *uri, void *provctx)169{170struct file_ctx_st *ctx;171172if ((ctx = new_file_ctx(IS_DIR, uri, provctx)) == NULL) {173ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB);174return NULL;175}176177ctx->_.dir.last_entry = OPENSSL_DIR_read(&ctx->_.dir.ctx, path);178ctx->_.dir.last_errno = errno;179if (ctx->_.dir.last_entry == NULL) {180if (ctx->_.dir.last_errno != 0) {181ERR_raise_data(ERR_LIB_SYS, ctx->_.dir.last_errno,182"Calling OPENSSL_DIR_read(\"%s\")", path);183goto err;184}185ctx->_.dir.end_reached = 1;186}187return ctx;188err:189file_close(ctx);190return NULL;191}192193static void *file_open(void *provctx, const char *uri)194{195struct file_ctx_st *ctx = NULL;196struct stat st;197struct {198const char *path;199unsigned int check_absolute : 1;200} path_data[2];201size_t path_data_n = 0, i;202const char *path, *p = uri, *q;203BIO *bio;204205ERR_set_mark();206207/*208* First step, just take the URI as is.209*/210path_data[path_data_n].check_absolute = 0;211path_data[path_data_n++].path = uri;212213/*214* Second step, if the URI appears to start with the "file" scheme,215* extract the path and make that the second path to check.216* There's a special case if the URI also contains an authority, then217* the full URI shouldn't be used as a path anywhere.218*/219if (CHECK_AND_SKIP_CASE_PREFIX(p, "file:")) {220q = p;221if (CHECK_AND_SKIP_CASE_PREFIX(q, "//")) {222path_data_n--; /* Invalidate using the full URI */223if (CHECK_AND_SKIP_CASE_PREFIX(q, "localhost/")224|| CHECK_AND_SKIP_CASE_PREFIX(q, "/")) {225p = q - 1;226} else {227ERR_clear_last_mark();228ERR_raise(ERR_LIB_PROV, PROV_R_URI_AUTHORITY_UNSUPPORTED);229return NULL;230}231}232233path_data[path_data_n].check_absolute = 1;234#ifdef _WIN32235/* Windows "file:" URIs with a drive letter start with a '/' */236if (p[0] == '/' && p[2] == ':' && p[3] == '/') {237char c = tolower((unsigned char)p[1]);238239if (c >= 'a' && c <= 'z') {240p++;241/* We know it's absolute, so no need to check */242path_data[path_data_n].check_absolute = 0;243}244}245#endif246path_data[path_data_n++].path = p;247}248249for (i = 0, path = NULL; path == NULL && i < path_data_n; i++) {250/*251* If the scheme "file" was an explicit part of the URI, the path must252* be absolute. So says RFC 8089253*/254if (path_data[i].check_absolute && path_data[i].path[0] != '/') {255ERR_clear_last_mark();256ERR_raise_data(ERR_LIB_PROV, PROV_R_PATH_MUST_BE_ABSOLUTE,257"Given path=%s", path_data[i].path);258return NULL;259}260261if (stat(path_data[i].path, &st) < 0) {262ERR_raise_data(ERR_LIB_SYS, errno,263"calling stat(%s)",264path_data[i].path);265} else {266path = path_data[i].path;267}268}269if (path == NULL) {270ERR_clear_last_mark();271return NULL;272}273274/* Successfully found a working path, clear possible collected errors */275ERR_pop_to_mark();276277if (S_ISDIR(st.st_mode))278ctx = file_open_dir(path, uri, provctx);279else if ((bio = BIO_new_file(path, "rb")) == NULL280|| (ctx = file_open_stream(bio, uri, provctx)) == NULL)281BIO_free_all(bio);282283return ctx;284}285286void *file_attach(void *provctx, OSSL_CORE_BIO *cin)287{288struct file_ctx_st *ctx;289BIO *new_bio = ossl_bio_new_from_core_bio(provctx, cin);290291if (new_bio == NULL)292return NULL;293294ctx = file_open_stream(new_bio, NULL, provctx);295if (ctx == NULL)296BIO_free(new_bio);297return ctx;298}299300/*-301* Setting parameters302* ------------------303*/304305static const OSSL_PARAM *file_settable_ctx_params(void *provctx)306{307static const OSSL_PARAM known_settable_ctx_params[] = {308OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_PROPERTIES, NULL, 0),309OSSL_PARAM_int(OSSL_STORE_PARAM_EXPECT, NULL),310OSSL_PARAM_octet_string(OSSL_STORE_PARAM_SUBJECT, NULL, 0),311OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_INPUT_TYPE, NULL, 0),312OSSL_PARAM_END313};314return known_settable_ctx_params;315}316317static int file_set_ctx_params(void *loaderctx, const OSSL_PARAM params[])318{319struct file_ctx_st *ctx = loaderctx;320const OSSL_PARAM *p;321322if (ossl_param_is_empty(params))323return 1;324325if (ctx->type != IS_DIR) {326/* these parameters are ignored for directories */327p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_PROPERTIES);328if (p != NULL) {329OPENSSL_free(ctx->_.file.propq);330ctx->_.file.propq = NULL;331if (!OSSL_PARAM_get_utf8_string(p, &ctx->_.file.propq, 0))332return 0;333}334p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_INPUT_TYPE);335if (p != NULL) {336OPENSSL_free(ctx->_.file.input_type);337ctx->_.file.input_type = NULL;338if (!OSSL_PARAM_get_utf8_string(p, &ctx->_.file.input_type, 0))339return 0;340}341}342p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_EXPECT);343if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->expected_type))344return 0;345p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_SUBJECT);346if (p != NULL) {347const unsigned char *der = NULL;348size_t der_len = 0;349X509_NAME *x509_name;350unsigned long hash;351int ok;352353if (ctx->type != IS_DIR) {354ERR_raise(ERR_LIB_PROV,355PROV_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES);356return 0;357}358359if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&der, &der_len)360|| (x509_name = d2i_X509_NAME(NULL, &der, der_len)) == NULL)361return 0;362hash = X509_NAME_hash_ex(x509_name,363ossl_prov_ctx_get0_libctx(ctx->provctx), NULL,364&ok);365BIO_snprintf(ctx->_.dir.search_name, sizeof(ctx->_.dir.search_name),366"%08lx", hash);367X509_NAME_free(x509_name);368if (ok == 0)369return 0;370}371return 1;372}373374/*-375* Loading an object from a stream376* -------------------------------377*/378379struct file_load_data_st {380OSSL_CALLBACK *object_cb;381void *object_cbarg;382};383384static int file_load_construct(OSSL_DECODER_INSTANCE *decoder_inst,385const OSSL_PARAM *params, void *construct_data)386{387struct file_load_data_st *data = construct_data;388389/*390* At some point, we may find it justifiable to recognise PKCS#12 and391* handle it specially here, making |file_load()| return pass its392* contents one piece at ta time, like |e_loader_attic.c| does.393*394* However, that currently means parsing them out, which converts the395* DER encoded PKCS#12 into a bunch of EVP_PKEYs and X509s, just to396* have to re-encode them into DER to create an object abstraction for397* each of them.398* It's much simpler (less churn) to pass on the object abstraction we399* get to the load_result callback and leave it to that one to do the400* work. If that's libcrypto code, we know that it has much better401* possibilities to handle the EVP_PKEYs and X509s without the extra402* churn.403*/404405return data->object_cb(params, data->object_cbarg);406}407408void file_load_cleanup(void *construct_data)409{410/* Nothing to do */411}412413static int file_setup_decoders(struct file_ctx_st *ctx)414{415OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(ctx->provctx);416const OSSL_ALGORITHM *to_algo = NULL;417const char *input_structure = NULL;418int ok = 0;419420/* Setup for this session, so only if not already done */421if (ctx->_.file.decoderctx == NULL) {422if ((ctx->_.file.decoderctx = OSSL_DECODER_CTX_new()) == NULL) {423ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);424goto err;425}426427/* Make sure the input type is set */428if (!OSSL_DECODER_CTX_set_input_type(ctx->_.file.decoderctx,429ctx->_.file.input_type)) {430ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);431goto err;432}433434/*435* Where applicable, set the outermost structure name.436* The goal is to avoid the STORE object types that are437* potentially password protected but aren't interesting438* for this load.439*/440switch (ctx->expected_type) {441case OSSL_STORE_INFO_PUBKEY:442input_structure = "SubjectPublicKeyInfo";443if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,444input_structure)) {445ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);446goto err;447}448break;449case OSSL_STORE_INFO_PKEY:450/*451* The user's OSSL_STORE_INFO_PKEY covers PKCS#8, whether encrypted452* or not. The decoder will figure out whether decryption is453* applicable and fall back as necessary. We just need to indicate454* that it is OK to try and encrypt, which may involve a password455* prompt, so not done unless the data type is explicit, as we456* might then get a password prompt for a key when reading only457* certs from a file.458*/459input_structure = "EncryptedPrivateKeyInfo";460if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,461input_structure)) {462ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);463goto err;464}465break;466case OSSL_STORE_INFO_CERT:467input_structure = "Certificate";468if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,469input_structure)) {470ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);471goto err;472}473break;474case OSSL_STORE_INFO_CRL:475input_structure = "CertificateList";476if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,477input_structure)) {478ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);479goto err;480}481break;482default:483break;484}485486for (to_algo = ossl_any_to_obj_algorithm;487to_algo->algorithm_names != NULL;488to_algo++) {489OSSL_DECODER *to_obj = NULL;490OSSL_DECODER_INSTANCE *to_obj_inst = NULL;491const char *input_type;492493/*494* Create the internal last resort decoder implementation495* together with a "decoder instance".496* The decoder doesn't need any identification or to be497* attached to any provider, since it's only used locally.498*/499to_obj = ossl_decoder_from_algorithm(0, to_algo, NULL);500if (to_obj != NULL)501to_obj_inst = ossl_decoder_instance_new_forprov(to_obj, ctx->provctx,502input_structure);503OSSL_DECODER_free(to_obj);504if (to_obj_inst == NULL)505goto err;506/*507* The input type has to match unless, the input type is PEM508* and the decoder input type is DER, in which case we'll pick509* up additional decoders.510*/511input_type = OSSL_DECODER_INSTANCE_get_input_type(to_obj_inst);512if (ctx->_.file.input_type != NULL513&& OPENSSL_strcasecmp(input_type, ctx->_.file.input_type) != 0514&& (OPENSSL_strcasecmp(ctx->_.file.input_type, "PEM") != 0515|| OPENSSL_strcasecmp(input_type, "der") != 0)) {516ossl_decoder_instance_free(to_obj_inst);517continue;518}519520if (!ossl_decoder_ctx_add_decoder_inst(ctx->_.file.decoderctx,521to_obj_inst)) {522ossl_decoder_instance_free(to_obj_inst);523ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);524goto err;525}526}527/* Add on the usual extra decoders */528if (!OSSL_DECODER_CTX_add_extra(ctx->_.file.decoderctx,529libctx, ctx->_.file.propq)) {530ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);531goto err;532}533534/*535* Then install our constructor hooks, which just passes decoded536* data to the load callback537*/538if (!OSSL_DECODER_CTX_set_construct(ctx->_.file.decoderctx,539file_load_construct)540|| !OSSL_DECODER_CTX_set_cleanup(ctx->_.file.decoderctx,541file_load_cleanup)) {542ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);543goto err;544}545}546547ok = 1;548err:549return ok;550}551552static int file_load_file(struct file_ctx_st *ctx,553OSSL_CALLBACK *object_cb, void *object_cbarg,554OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)555{556struct file_load_data_st data;557int ret, err;558559/* Setup the decoders (one time shot per session */560561if (!file_setup_decoders(ctx))562return 0;563564/* Setup for this object */565566data.object_cb = object_cb;567data.object_cbarg = object_cbarg;568OSSL_DECODER_CTX_set_construct_data(ctx->_.file.decoderctx, &data);569OSSL_DECODER_CTX_set_passphrase_cb(ctx->_.file.decoderctx, pw_cb, pw_cbarg);570571/* Launch */572573ERR_set_mark();574ret = OSSL_DECODER_from_bio(ctx->_.file.decoderctx, ctx->_.file.file);575if (BIO_eof(ctx->_.file.file)576&& ((err = ERR_peek_last_error()) != 0)577&& ERR_GET_LIB(err) == ERR_LIB_OSSL_DECODER578&& ERR_GET_REASON(err) == ERR_R_UNSUPPORTED)579ERR_pop_to_mark();580else581ERR_clear_last_mark();582return ret;583}584585/*-586* Loading a name object from a directory587* --------------------------------------588*/589590static char *file_name_to_uri(struct file_ctx_st *ctx, const char *name)591{592char *data = NULL;593594assert(name != NULL);595{596const char *pathsep = ossl_ends_with_dirsep(ctx->uri) ? "" : "/";597long calculated_length = strlen(ctx->uri) + strlen(pathsep)598+ strlen(name) + 1 /* \0 */;599600data = OPENSSL_zalloc(calculated_length);601if (data == NULL)602return NULL;603604OPENSSL_strlcat(data, ctx->uri, calculated_length);605OPENSSL_strlcat(data, pathsep, calculated_length);606OPENSSL_strlcat(data, name, calculated_length);607}608return data;609}610611static int file_name_check(struct file_ctx_st *ctx, const char *name)612{613const char *p = NULL;614size_t len = strlen(ctx->_.dir.search_name);615616/* If there are no search criteria, all names are accepted */617if (ctx->_.dir.search_name[0] == '\0')618return 1;619620/* If the expected type isn't supported, no name is accepted */621if (ctx->expected_type != 0622&& ctx->expected_type != OSSL_STORE_INFO_CERT623&& ctx->expected_type != OSSL_STORE_INFO_CRL)624return 0;625626/*627* First, check the basename628*/629if (OPENSSL_strncasecmp(name, ctx->_.dir.search_name, len) != 0630|| name[len] != '.')631return 0;632p = &name[len + 1];633634/*635* Then, if the expected type is a CRL, check that the extension starts636* with 'r'637*/638if (*p == 'r') {639p++;640if (ctx->expected_type != 0641&& ctx->expected_type != OSSL_STORE_INFO_CRL)642return 0;643} else if (ctx->expected_type == OSSL_STORE_INFO_CRL) {644return 0;645}646647/*648* Last, check that the rest of the extension is a decimal number, at649* least one digit long.650*/651if (!isdigit((unsigned char)*p))652return 0;653while (isdigit((unsigned char)*p))654p++;655656#ifdef __VMS657/*658* One extra step here, check for a possible generation number.659*/660if (*p == ';')661for (p++; *p != '\0'; p++)662if (!ossl_isdigit((unsigned char)*p))663break;664#endif665666/*667* If we've reached the end of the string at this point, we've successfully668* found a fitting file name.669*/670return *p == '\0';671}672673static int file_load_dir_entry(struct file_ctx_st *ctx,674OSSL_CALLBACK *object_cb, void *object_cbarg,675OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)676{677/* Prepare as much as possible in advance */678static const int object_type = OSSL_OBJECT_NAME;679OSSL_PARAM object[] = {680OSSL_PARAM_int(OSSL_OBJECT_PARAM_TYPE, (int *)&object_type),681OSSL_PARAM_utf8_string(OSSL_OBJECT_PARAM_DATA, NULL, 0),682OSSL_PARAM_END683};684char *newname = NULL;685int ok;686687/* Loop until we get an error or until we have a suitable name */688do {689if (ctx->_.dir.last_entry == NULL) {690if (!ctx->_.dir.end_reached) {691assert(ctx->_.dir.last_errno != 0);692ERR_raise(ERR_LIB_SYS, ctx->_.dir.last_errno);693}694/* file_eof() will tell if EOF was reached */695return 0;696}697698/* flag acceptable names */699if (ctx->_.dir.last_entry[0] != '.'700&& file_name_check(ctx, ctx->_.dir.last_entry)) {701702/* If we can't allocate the new name, we fail */703if ((newname = file_name_to_uri(ctx, ctx->_.dir.last_entry)) == NULL)704return 0;705}706707/*708* On the first call (with a NULL context), OPENSSL_DIR_read()709* cares about the second argument. On the following calls, it710* only cares that it isn't NULL. Therefore, we can safely give711* it our URI here.712*/713ctx->_.dir.last_entry = OPENSSL_DIR_read(&ctx->_.dir.ctx, ctx->uri);714ctx->_.dir.last_errno = errno;715if (ctx->_.dir.last_entry == NULL && ctx->_.dir.last_errno == 0)716ctx->_.dir.end_reached = 1;717} while (newname == NULL);718719object[1].data = newname;720object[1].data_size = strlen(newname);721ok = object_cb(object, object_cbarg);722OPENSSL_free(newname);723return ok;724}725726/*-727* Loading, local dispatcher728* -------------------------729*/730731static int file_load(void *loaderctx,732OSSL_CALLBACK *object_cb, void *object_cbarg,733OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)734{735struct file_ctx_st *ctx = loaderctx;736737switch (ctx->type) {738case IS_FILE:739return file_load_file(ctx, object_cb, object_cbarg, pw_cb, pw_cbarg);740case IS_DIR:741return file_load_dir_entry(ctx, object_cb, object_cbarg, pw_cb, pw_cbarg);742default:743break;744}745746/* ctx->type has an unexpected value */747assert(0);748return 0;749}750751/*-752* Eof detection and closing753* -------------------------754*/755756static int file_eof(void *loaderctx)757{758struct file_ctx_st *ctx = loaderctx;759760switch (ctx->type) {761case IS_DIR:762return ctx->_.dir.end_reached;763case IS_FILE:764/*765* BIO_pending() checks any filter BIO.766* BIO_eof() checks the source BIO.767*/768return !BIO_pending(ctx->_.file.file)769&& BIO_eof(ctx->_.file.file);770}771772/* ctx->type has an unexpected value */773assert(0);774return 1;775}776777static int file_close_dir(struct file_ctx_st *ctx)778{779if (ctx->_.dir.ctx != NULL)780OPENSSL_DIR_end(&ctx->_.dir.ctx);781free_file_ctx(ctx);782return 1;783}784785static int file_close_stream(struct file_ctx_st *ctx)786{787/*788* This frees either the provider BIO filter (for file_attach()) OR789* the allocated file BIO (for file_open()).790*/791BIO_free(ctx->_.file.file);792ctx->_.file.file = NULL;793794free_file_ctx(ctx);795return 1;796}797798static int file_close(void *loaderctx)799{800struct file_ctx_st *ctx = loaderctx;801802switch (ctx->type) {803case IS_DIR:804return file_close_dir(ctx);805case IS_FILE:806return file_close_stream(ctx);807}808809/* ctx->type has an unexpected value */810assert(0);811return 1;812}813814const OSSL_DISPATCH ossl_file_store_functions[] = {815{ OSSL_FUNC_STORE_OPEN, (void (*)(void))file_open },816{ OSSL_FUNC_STORE_ATTACH, (void (*)(void))file_attach },817{ OSSL_FUNC_STORE_SETTABLE_CTX_PARAMS,818(void (*)(void))file_settable_ctx_params },819{ OSSL_FUNC_STORE_SET_CTX_PARAMS, (void (*)(void))file_set_ctx_params },820{ OSSL_FUNC_STORE_LOAD, (void (*)(void))file_load },821{ OSSL_FUNC_STORE_EOF, (void (*)(void))file_eof },822{ OSSL_FUNC_STORE_CLOSE, (void (*)(void))file_close },823OSSL_DISPATCH_END,824};825826827