Path: blob/main/crypto/openssl/providers/implementations/storemgmt/file_store.c
34371 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}248249250for (i = 0, path = NULL; path == NULL && i < path_data_n; i++) {251/*252* If the scheme "file" was an explicit part of the URI, the path must253* be absolute. So says RFC 8089254*/255if (path_data[i].check_absolute && path_data[i].path[0] != '/') {256ERR_clear_last_mark();257ERR_raise_data(ERR_LIB_PROV, PROV_R_PATH_MUST_BE_ABSOLUTE,258"Given path=%s", path_data[i].path);259return NULL;260}261262if (stat(path_data[i].path, &st) < 0) {263ERR_raise_data(ERR_LIB_SYS, errno,264"calling stat(%s)",265path_data[i].path);266} else {267path = path_data[i].path;268}269}270if (path == NULL) {271ERR_clear_last_mark();272return NULL;273}274275/* Successfully found a working path, clear possible collected errors */276ERR_pop_to_mark();277278if (S_ISDIR(st.st_mode))279ctx = file_open_dir(path, uri, provctx);280else if ((bio = BIO_new_file(path, "rb")) == NULL281|| (ctx = file_open_stream(bio, uri, provctx)) == NULL)282BIO_free_all(bio);283284return ctx;285}286287void *file_attach(void *provctx, OSSL_CORE_BIO *cin)288{289struct file_ctx_st *ctx;290BIO *new_bio = ossl_bio_new_from_core_bio(provctx, cin);291292if (new_bio == NULL)293return NULL;294295ctx = file_open_stream(new_bio, NULL, provctx);296if (ctx == NULL)297BIO_free(new_bio);298return ctx;299}300301/*-302* Setting parameters303* ------------------304*/305306static const OSSL_PARAM *file_settable_ctx_params(void *provctx)307{308static const OSSL_PARAM known_settable_ctx_params[] = {309OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_PROPERTIES, NULL, 0),310OSSL_PARAM_int(OSSL_STORE_PARAM_EXPECT, NULL),311OSSL_PARAM_octet_string(OSSL_STORE_PARAM_SUBJECT, NULL, 0),312OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_INPUT_TYPE, NULL, 0),313OSSL_PARAM_END314};315return known_settable_ctx_params;316}317318static int file_set_ctx_params(void *loaderctx, const OSSL_PARAM params[])319{320struct file_ctx_st *ctx = loaderctx;321const OSSL_PARAM *p;322323if (ossl_param_is_empty(params))324return 1;325326if (ctx->type != IS_DIR) {327/* these parameters are ignored for directories */328p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_PROPERTIES);329if (p != NULL) {330OPENSSL_free(ctx->_.file.propq);331ctx->_.file.propq = NULL;332if (!OSSL_PARAM_get_utf8_string(p, &ctx->_.file.propq, 0))333return 0;334}335p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_INPUT_TYPE);336if (p != NULL) {337OPENSSL_free(ctx->_.file.input_type);338ctx->_.file.input_type = NULL;339if (!OSSL_PARAM_get_utf8_string(p, &ctx->_.file.input_type, 0))340return 0;341}342}343p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_EXPECT);344if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->expected_type))345return 0;346p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_SUBJECT);347if (p != NULL) {348const unsigned char *der = NULL;349size_t der_len = 0;350X509_NAME *x509_name;351unsigned long hash;352int ok;353354if (ctx->type != IS_DIR) {355ERR_raise(ERR_LIB_PROV,356PROV_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES);357return 0;358}359360if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&der, &der_len)361|| (x509_name = d2i_X509_NAME(NULL, &der, der_len)) == NULL)362return 0;363hash = X509_NAME_hash_ex(x509_name,364ossl_prov_ctx_get0_libctx(ctx->provctx), NULL,365&ok);366BIO_snprintf(ctx->_.dir.search_name, sizeof(ctx->_.dir.search_name),367"%08lx", hash);368X509_NAME_free(x509_name);369if (ok == 0)370return 0;371}372return 1;373}374375/*-376* Loading an object from a stream377* -------------------------------378*/379380struct file_load_data_st {381OSSL_CALLBACK *object_cb;382void *object_cbarg;383};384385static int file_load_construct(OSSL_DECODER_INSTANCE *decoder_inst,386const OSSL_PARAM *params, void *construct_data)387{388struct file_load_data_st *data = construct_data;389390/*391* At some point, we may find it justifiable to recognise PKCS#12 and392* handle it specially here, making |file_load()| return pass its393* contents one piece at ta time, like |e_loader_attic.c| does.394*395* However, that currently means parsing them out, which converts the396* DER encoded PKCS#12 into a bunch of EVP_PKEYs and X509s, just to397* have to re-encode them into DER to create an object abstraction for398* each of them.399* It's much simpler (less churn) to pass on the object abstraction we400* get to the load_result callback and leave it to that one to do the401* work. If that's libcrypto code, we know that it has much better402* possibilities to handle the EVP_PKEYs and X509s without the extra403* churn.404*/405406return data->object_cb(params, data->object_cbarg);407}408409void file_load_cleanup(void *construct_data)410{411/* Nothing to do */412}413414static int file_setup_decoders(struct file_ctx_st *ctx)415{416OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(ctx->provctx);417const OSSL_ALGORITHM *to_algo = NULL;418const char *input_structure = NULL;419int ok = 0;420421/* Setup for this session, so only if not already done */422if (ctx->_.file.decoderctx == NULL) {423if ((ctx->_.file.decoderctx = OSSL_DECODER_CTX_new()) == NULL) {424ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);425goto err;426}427428/* Make sure the input type is set */429if (!OSSL_DECODER_CTX_set_input_type(ctx->_.file.decoderctx,430ctx->_.file.input_type)) {431ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);432goto err;433}434435/*436* Where applicable, set the outermost structure name.437* The goal is to avoid the STORE object types that are438* potentially password protected but aren't interesting439* for this load.440*/441switch (ctx->expected_type) {442case OSSL_STORE_INFO_PUBKEY:443input_structure = "SubjectPublicKeyInfo";444if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,445input_structure)) {446ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);447goto err;448}449break;450case OSSL_STORE_INFO_PKEY:451/*452* The user's OSSL_STORE_INFO_PKEY covers PKCS#8, whether encrypted453* or not. The decoder will figure out whether decryption is454* applicable and fall back as necessary. We just need to indicate455* that it is OK to try and encrypt, which may involve a password456* prompt, so not done unless the data type is explicit, as we457* might then get a password prompt for a key when reading only458* certs from a file.459*/460input_structure = "EncryptedPrivateKeyInfo";461if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,462input_structure)) {463ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);464goto err;465}466break;467case OSSL_STORE_INFO_CERT:468input_structure = "Certificate";469if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,470input_structure)) {471ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);472goto err;473}474break;475case OSSL_STORE_INFO_CRL:476input_structure = "CertificateList";477if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,478input_structure)) {479ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);480goto err;481}482break;483default:484break;485}486487for (to_algo = ossl_any_to_obj_algorithm;488to_algo->algorithm_names != NULL;489to_algo++) {490OSSL_DECODER *to_obj = NULL;491OSSL_DECODER_INSTANCE *to_obj_inst = NULL;492const char *input_type;493494/*495* Create the internal last resort decoder implementation496* together with a "decoder instance".497* The decoder doesn't need any identification or to be498* attached to any provider, since it's only used locally.499*/500to_obj = ossl_decoder_from_algorithm(0, to_algo, NULL);501if (to_obj != NULL)502to_obj_inst =503ossl_decoder_instance_new_forprov(to_obj, ctx->provctx,504input_structure);505OSSL_DECODER_free(to_obj);506if (to_obj_inst == NULL)507goto err;508/*509* The input type has to match unless, the input type is PEM510* and the decoder input type is DER, in which case we'll pick511* up additional decoders.512*/513input_type = OSSL_DECODER_INSTANCE_get_input_type(to_obj_inst);514if (ctx->_.file.input_type != NULL515&& OPENSSL_strcasecmp(input_type, ctx->_.file.input_type) != 0516&& (OPENSSL_strcasecmp(ctx->_.file.input_type, "PEM") != 0517|| OPENSSL_strcasecmp(input_type, "der") != 0)) {518ossl_decoder_instance_free(to_obj_inst);519continue;520}521522if (!ossl_decoder_ctx_add_decoder_inst(ctx->_.file.decoderctx,523to_obj_inst)) {524ossl_decoder_instance_free(to_obj_inst);525ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);526goto err;527}528}529/* Add on the usual extra decoders */530if (!OSSL_DECODER_CTX_add_extra(ctx->_.file.decoderctx,531libctx, ctx->_.file.propq)) {532ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);533goto err;534}535536/*537* Then install our constructor hooks, which just passes decoded538* data to the load callback539*/540if (!OSSL_DECODER_CTX_set_construct(ctx->_.file.decoderctx,541file_load_construct)542|| !OSSL_DECODER_CTX_set_cleanup(ctx->_.file.decoderctx,543file_load_cleanup)) {544ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);545goto err;546}547}548549ok = 1;550err:551return ok;552}553554static int file_load_file(struct file_ctx_st *ctx,555OSSL_CALLBACK *object_cb, void *object_cbarg,556OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)557{558struct file_load_data_st data;559int ret, err;560561/* Setup the decoders (one time shot per session */562563if (!file_setup_decoders(ctx))564return 0;565566/* Setup for this object */567568data.object_cb = object_cb;569data.object_cbarg = object_cbarg;570OSSL_DECODER_CTX_set_construct_data(ctx->_.file.decoderctx, &data);571OSSL_DECODER_CTX_set_passphrase_cb(ctx->_.file.decoderctx, pw_cb, pw_cbarg);572573/* Launch */574575ERR_set_mark();576ret = OSSL_DECODER_from_bio(ctx->_.file.decoderctx, ctx->_.file.file);577if (BIO_eof(ctx->_.file.file)578&& ((err = ERR_peek_last_error()) != 0)579&& ERR_GET_LIB(err) == ERR_LIB_OSSL_DECODER580&& ERR_GET_REASON(err) == ERR_R_UNSUPPORTED)581ERR_pop_to_mark();582else583ERR_clear_last_mark();584return ret;585}586587/*-588* Loading a name object from a directory589* --------------------------------------590*/591592static char *file_name_to_uri(struct file_ctx_st *ctx, const char *name)593{594char *data = NULL;595596assert(name != NULL);597{598const char *pathsep = ossl_ends_with_dirsep(ctx->uri) ? "" : "/";599long calculated_length = strlen(ctx->uri) + strlen(pathsep)600+ strlen(name) + 1 /* \0 */;601602data = OPENSSL_zalloc(calculated_length);603if (data == NULL)604return NULL;605606OPENSSL_strlcat(data, ctx->uri, calculated_length);607OPENSSL_strlcat(data, pathsep, calculated_length);608OPENSSL_strlcat(data, name, calculated_length);609}610return data;611}612613static int file_name_check(struct file_ctx_st *ctx, const char *name)614{615const char *p = NULL;616size_t len = strlen(ctx->_.dir.search_name);617618/* If there are no search criteria, all names are accepted */619if (ctx->_.dir.search_name[0] == '\0')620return 1;621622/* If the expected type isn't supported, no name is accepted */623if (ctx->expected_type != 0624&& ctx->expected_type != OSSL_STORE_INFO_CERT625&& ctx->expected_type != OSSL_STORE_INFO_CRL)626return 0;627628/*629* First, check the basename630*/631if (OPENSSL_strncasecmp(name, ctx->_.dir.search_name, len) != 0632|| name[len] != '.')633return 0;634p = &name[len + 1];635636/*637* Then, if the expected type is a CRL, check that the extension starts638* with 'r'639*/640if (*p == 'r') {641p++;642if (ctx->expected_type != 0643&& ctx->expected_type != OSSL_STORE_INFO_CRL)644return 0;645} else if (ctx->expected_type == OSSL_STORE_INFO_CRL) {646return 0;647}648649/*650* Last, check that the rest of the extension is a decimal number, at651* least one digit long.652*/653if (!isdigit((unsigned char)*p))654return 0;655while (isdigit((unsigned char)*p))656p++;657658#ifdef __VMS659/*660* One extra step here, check for a possible generation number.661*/662if (*p == ';')663for (p++; *p != '\0'; p++)664if (!ossl_isdigit((unsigned char)*p))665break;666#endif667668/*669* If we've reached the end of the string at this point, we've successfully670* found a fitting file name.671*/672return *p == '\0';673}674675static int file_load_dir_entry(struct file_ctx_st *ctx,676OSSL_CALLBACK *object_cb, void *object_cbarg,677OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)678{679/* Prepare as much as possible in advance */680static const int object_type = OSSL_OBJECT_NAME;681OSSL_PARAM object[] = {682OSSL_PARAM_int(OSSL_OBJECT_PARAM_TYPE, (int *)&object_type),683OSSL_PARAM_utf8_string(OSSL_OBJECT_PARAM_DATA, NULL, 0),684OSSL_PARAM_END685};686char *newname = NULL;687int ok;688689/* Loop until we get an error or until we have a suitable name */690do {691if (ctx->_.dir.last_entry == NULL) {692if (!ctx->_.dir.end_reached) {693assert(ctx->_.dir.last_errno != 0);694ERR_raise(ERR_LIB_SYS, ctx->_.dir.last_errno);695}696/* file_eof() will tell if EOF was reached */697return 0;698}699700/* flag acceptable names */701if (ctx->_.dir.last_entry[0] != '.'702&& file_name_check(ctx, ctx->_.dir.last_entry)) {703704/* If we can't allocate the new name, we fail */705if ((newname =706file_name_to_uri(ctx, ctx->_.dir.last_entry)) == NULL)707return 0;708}709710/*711* On the first call (with a NULL context), OPENSSL_DIR_read()712* cares about the second argument. On the following calls, it713* only cares that it isn't NULL. Therefore, we can safely give714* it our URI here.715*/716ctx->_.dir.last_entry = OPENSSL_DIR_read(&ctx->_.dir.ctx, ctx->uri);717ctx->_.dir.last_errno = errno;718if (ctx->_.dir.last_entry == NULL && ctx->_.dir.last_errno == 0)719ctx->_.dir.end_reached = 1;720} while (newname == NULL);721722object[1].data = newname;723object[1].data_size = strlen(newname);724ok = object_cb(object, object_cbarg);725OPENSSL_free(newname);726return ok;727}728729/*-730* Loading, local dispatcher731* -------------------------732*/733734static int file_load(void *loaderctx,735OSSL_CALLBACK *object_cb, void *object_cbarg,736OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)737{738struct file_ctx_st *ctx = loaderctx;739740switch (ctx->type) {741case IS_FILE:742return file_load_file(ctx, object_cb, object_cbarg, pw_cb, pw_cbarg);743case IS_DIR:744return745file_load_dir_entry(ctx, object_cb, object_cbarg, pw_cb, pw_cbarg);746default:747break;748}749750/* ctx->type has an unexpected value */751assert(0);752return 0;753}754755/*-756* Eof detection and closing757* -------------------------758*/759760static int file_eof(void *loaderctx)761{762struct file_ctx_st *ctx = loaderctx;763764switch (ctx->type) {765case IS_DIR:766return ctx->_.dir.end_reached;767case IS_FILE:768/*769* BIO_pending() checks any filter BIO.770* BIO_eof() checks the source BIO.771*/772return !BIO_pending(ctx->_.file.file)773&& BIO_eof(ctx->_.file.file);774}775776/* ctx->type has an unexpected value */777assert(0);778return 1;779}780781static int file_close_dir(struct file_ctx_st *ctx)782{783if (ctx->_.dir.ctx != NULL)784OPENSSL_DIR_end(&ctx->_.dir.ctx);785free_file_ctx(ctx);786return 1;787}788789static int file_close_stream(struct file_ctx_st *ctx)790{791/*792* This frees either the provider BIO filter (for file_attach()) OR793* the allocated file BIO (for file_open()).794*/795BIO_free(ctx->_.file.file);796ctx->_.file.file = NULL;797798free_file_ctx(ctx);799return 1;800}801802static int file_close(void *loaderctx)803{804struct file_ctx_st *ctx = loaderctx;805806switch (ctx->type) {807case IS_DIR:808return file_close_dir(ctx);809case IS_FILE:810return file_close_stream(ctx);811}812813/* ctx->type has an unexpected value */814assert(0);815return 1;816}817818const OSSL_DISPATCH ossl_file_store_functions[] = {819{ OSSL_FUNC_STORE_OPEN, (void (*)(void))file_open },820{ OSSL_FUNC_STORE_ATTACH, (void (*)(void))file_attach },821{ OSSL_FUNC_STORE_SETTABLE_CTX_PARAMS,822(void (*)(void))file_settable_ctx_params },823{ OSSL_FUNC_STORE_SET_CTX_PARAMS, (void (*)(void))file_set_ctx_params },824{ OSSL_FUNC_STORE_LOAD, (void (*)(void))file_load },825{ OSSL_FUNC_STORE_EOF, (void (*)(void))file_eof },826{ OSSL_FUNC_STORE_CLOSE, (void (*)(void))file_close },827OSSL_DISPATCH_END,828};829830831