| /* |
| * 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 |
| */ |
| |
| #include "e_os.h" /* strcasecmp on Windows */ |
| #include <openssl/core_names.h> |
| #include <openssl/bio.h> |
| #include <openssl/encoder.h> |
| #include <openssl/buffer.h> |
| #include <openssl/params.h> |
| #include <openssl/provider.h> |
| #include "encoder_local.h" |
| |
| static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out); |
| |
| int OSSL_ENCODER_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out) |
| { |
| return encoder_process(ctx, out); |
| } |
| |
| #ifndef OPENSSL_NO_STDIO |
| static BIO *bio_from_file(FILE *fp) |
| { |
| BIO *b; |
| |
| if ((b = BIO_new(BIO_s_file())) == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_BUF_LIB); |
| return NULL; |
| } |
| BIO_set_fp(b, fp, BIO_NOCLOSE); |
| return b; |
| } |
| |
| int OSSL_ENCODER_to_fp(OSSL_ENCODER_CTX *ctx, FILE *fp) |
| { |
| BIO *b = bio_from_file(fp); |
| int ret = 0; |
| |
| if (b != NULL) |
| ret = OSSL_ENCODER_to_bio(ctx, b); |
| |
| BIO_free(b); |
| return ret; |
| } |
| #endif |
| |
| int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx, |
| const char *output_type) |
| { |
| if (!ossl_assert(ctx != NULL) || !ossl_assert(output_type != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| ctx->output_type = output_type; |
| return 1; |
| } |
| |
| int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection) |
| { |
| if (!ossl_assert(ctx != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| if (!ossl_assert(selection != 0)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_INVALID_ARGUMENT); |
| return 0; |
| } |
| |
| ctx->selection = selection; |
| return 1; |
| } |
| |
| static OSSL_ENCODER_INSTANCE *ossl_encoder_instance_new(OSSL_ENCODER *encoder, |
| void *encoderctx) |
| { |
| OSSL_ENCODER_INSTANCE *encoder_inst = NULL; |
| OSSL_PARAM params[3]; |
| |
| if (!ossl_assert(encoder != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| if (encoder->get_params == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, |
| OSSL_ENCODER_R_MISSING_GET_PARAMS); |
| return 0; |
| } |
| |
| if ((encoder_inst = OPENSSL_zalloc(sizeof(*encoder_inst))) == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE); |
| return 0; |
| } |
| |
| /* |
| * Cache the input and output types for this encoder. The output type |
| * is mandatory. |
| */ |
| params[0] = |
| OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_OUTPUT_TYPE, |
| (char **)&encoder_inst->output_type, 0); |
| params[1] = |
| OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_INPUT_TYPE, |
| (char **)&encoder_inst->input_type, 0); |
| params[2] = OSSL_PARAM_construct_end(); |
| |
| if (!encoder->get_params(params) |
| || !OSSL_PARAM_modified(¶ms[1])) |
| goto err; |
| |
| if (!OSSL_ENCODER_up_ref(encoder)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR); |
| goto err; |
| } |
| |
| encoder_inst->encoder = encoder; |
| encoder_inst->encoderctx = encoderctx; |
| return encoder_inst; |
| err: |
| ossl_encoder_instance_free(encoder_inst); |
| return NULL; |
| } |
| |
| void ossl_encoder_instance_free(OSSL_ENCODER_INSTANCE *encoder_inst) |
| { |
| if (encoder_inst != NULL) { |
| if (encoder_inst->encoder != NULL) |
| encoder_inst->encoder->freectx(encoder_inst->encoderctx); |
| encoder_inst->encoderctx = NULL; |
| OSSL_ENCODER_free(encoder_inst->encoder); |
| encoder_inst->encoder = NULL; |
| OPENSSL_free(encoder_inst); |
| } |
| } |
| |
| static int ossl_encoder_ctx_add_encoder_inst(OSSL_ENCODER_CTX *ctx, |
| OSSL_ENCODER_INSTANCE *ei) |
| { |
| if (ctx->encoder_insts == NULL |
| && (ctx->encoder_insts = |
| sk_OSSL_ENCODER_INSTANCE_new_null()) == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE); |
| return 0; |
| } |
| |
| return (sk_OSSL_ENCODER_INSTANCE_push(ctx->encoder_insts, ei) > 0); |
| } |
| |
| int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder) |
| { |
| OSSL_ENCODER_INSTANCE *encoder_inst = NULL; |
| const OSSL_PROVIDER *prov = NULL; |
| void *encoderctx = NULL; |
| void *provctx = NULL; |
| |
| if (!ossl_assert(ctx != NULL) || !ossl_assert(encoder != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| prov = OSSL_ENCODER_provider(encoder); |
| provctx = OSSL_PROVIDER_get0_provider_ctx(prov); |
| |
| if ((encoderctx = encoder->newctx(provctx)) == NULL |
| || (encoder_inst = |
| ossl_encoder_instance_new(encoder, encoderctx)) == NULL) |
| goto err; |
| /* Avoid double free of encoderctx on further errors */ |
| encoderctx = NULL; |
| |
| if (!ossl_encoder_ctx_add_encoder_inst(ctx, encoder_inst)) |
| goto err; |
| |
| return 1; |
| err: |
| ossl_encoder_instance_free(encoder_inst); |
| if (encoderctx != NULL) |
| encoder->freectx(encoderctx); |
| return 0; |
| } |
| |
| int OSSL_ENCODER_CTX_add_extra(OSSL_ENCODER_CTX *ctx, |
| OPENSSL_CTX *libctx, const char *propq) |
| { |
| return 1; |
| } |
| |
| int OSSL_ENCODER_CTX_get_num_encoders(OSSL_ENCODER_CTX *ctx) |
| { |
| if (ctx == NULL || ctx->encoder_insts == NULL) |
| return 0; |
| return sk_OSSL_ENCODER_INSTANCE_num(ctx->encoder_insts); |
| } |
| |
| int OSSL_ENCODER_CTX_set_construct(OSSL_ENCODER_CTX *ctx, |
| OSSL_ENCODER_CONSTRUCT *construct) |
| { |
| if (!ossl_assert(ctx != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| ctx->construct = construct; |
| return 1; |
| } |
| |
| int OSSL_ENCODER_CTX_set_construct_data(OSSL_ENCODER_CTX *ctx, |
| void *construct_data) |
| { |
| if (!ossl_assert(ctx != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| ctx->construct_data = construct_data; |
| return 1; |
| } |
| |
| int OSSL_ENCODER_CTX_set_cleanup(OSSL_ENCODER_CTX *ctx, |
| OSSL_ENCODER_CLEANUP *cleanup) |
| { |
| if (!ossl_assert(ctx != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| ctx->cleanup = cleanup; |
| return 1; |
| } |
| |
| OSSL_ENCODER * |
| OSSL_ENCODER_INSTANCE_get_encoder(OSSL_ENCODER_INSTANCE *encoder_inst) |
| { |
| if (encoder_inst == NULL) |
| return NULL; |
| return encoder_inst->encoder; |
| } |
| |
| void * |
| OSSL_ENCODER_INSTANCE_get_encoder_ctx(OSSL_ENCODER_INSTANCE *encoder_inst) |
| { |
| if (encoder_inst == NULL) |
| return NULL; |
| return encoder_inst->encoderctx; |
| } |
| |
| const char * |
| OSSL_ENCODER_INSTANCE_get_input_type(OSSL_ENCODER_INSTANCE *encoder_inst) |
| { |
| if (encoder_inst == NULL) |
| return NULL; |
| return encoder_inst->input_type; |
| } |
| |
| const char * |
| OSSL_ENCODER_INSTANCE_get_output_type(OSSL_ENCODER_INSTANCE *encoder_inst) |
| { |
| if (encoder_inst == NULL) |
| return NULL; |
| return encoder_inst->output_type; |
| } |
| |
| static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out) |
| { |
| size_t i, end; |
| void *latest_output = NULL; |
| size_t latest_output_length = 0; |
| const char *latest_output_type = NULL; |
| const char *last_input_type = NULL; |
| int ok = 0; |
| |
| end = OSSL_ENCODER_CTX_get_num_encoders(ctx); |
| for (i = 0; i < end; i++) { |
| OSSL_ENCODER_INSTANCE *encoder_inst = |
| sk_OSSL_ENCODER_INSTANCE_value(ctx->encoder_insts, i); |
| OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst); |
| void *encoderctx = OSSL_ENCODER_INSTANCE_get_encoder_ctx(encoder_inst); |
| const char *current_input_type = |
| OSSL_ENCODER_INSTANCE_get_input_type(encoder_inst); |
| const char *current_output_type = |
| OSSL_ENCODER_INSTANCE_get_output_type(encoder_inst); |
| BIO *current_out; |
| BIO *allocated_out = NULL; |
| const void *current_data = NULL; |
| OSSL_PARAM abstract[3]; |
| OSSL_PARAM *abstract_p; |
| const OSSL_PARAM *current_abstract = NULL; |
| |
| if (latest_output_type == NULL) { |
| /* |
| * This is the first iteration, so we prepare the object to be |
| * encoded |
| */ |
| |
| current_data = ctx->construct(encoder_inst, ctx->construct_data); |
| |
| /* Assume that the constructor recorded an error */ |
| if (current_data == NULL) |
| goto loop_end; |
| } else { |
| /* |
| * Check that the latest output type matches the currently |
| * considered encoder |
| */ |
| if (!OSSL_ENCODER_is_a(encoder, latest_output_type)) |
| continue; |
| |
| /* |
| * If there is a latest output type, there should be a latest output |
| */ |
| if (!ossl_assert(latest_output != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR); |
| goto loop_end; |
| } |
| |
| /* |
| * Create an object abstraction from the latest output, which was |
| * stolen from the previous round. |
| */ |
| abstract_p = abstract; |
| if (last_input_type != NULL) |
| *abstract_p++ = |
| OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, |
| (char *)last_input_type, 0); |
| *abstract_p++ = |
| OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA, |
| latest_output, |
| latest_output_length); |
| *abstract_p = OSSL_PARAM_construct_end(); |
| current_abstract = abstract; |
| } |
| |
| /* |
| * If the desired output type matches the output type of the currently |
| * considered encoder, we're setting up final output. Otherwise, set |
| * up an intermediary memory output. |
| */ |
| if (strcasecmp(ctx->output_type, current_output_type) == 0) |
| current_out = out; |
| else if ((current_out = allocated_out = BIO_new(BIO_s_mem())) == NULL) |
| goto loop_end; /* Assume BIO_new() recorded an error */ |
| |
| ok = encoder->encode(encoderctx, (OSSL_CORE_BIO *)current_out, |
| current_data, current_abstract, ctx->selection, |
| ossl_pw_passphrase_callback_enc, &ctx->pwdata); |
| |
| if (current_input_type != NULL) |
| last_input_type = current_input_type; |
| |
| if (!ok) |
| goto loop_end; |
| |
| OPENSSL_free(latest_output); |
| |
| /* |
| * Steal the output from the BIO_s_mem, if we did allocate one. |
| * That'll be the data for an object abstraction in the next round. |
| */ |
| if (allocated_out != NULL) { |
| BUF_MEM *buf; |
| |
| BIO_get_mem_ptr(allocated_out, &buf); |
| latest_output = buf->data; |
| latest_output_length = buf->length; |
| memset(buf, 0, sizeof(*buf)); |
| BIO_free(allocated_out); |
| } |
| |
| loop_end: |
| if (current_data != NULL) |
| ctx->cleanup(ctx->construct_data); |
| |
| if (ok) |
| break; |
| } |
| |
| OPENSSL_free(latest_output); |
| return ok; |
| } |