From 7524b7b748d5989f015bc4b9651be92dbcb375fd Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Fri, 10 Jul 2020 15:13:55 +0200 Subject: [PATCH] DESERIALIZER: Implement decryption of password protected objects This implements these functions: OSSL_DESERIALIZER_CTX_set_cipher() OSSL_DESERIALIZER_CTX_set_passphrase() OSSL_DESERIALIZER_CTX_set_passphrase_ui() OSSL_DESERIALIZER_CTX_set_passphrase_cb() To be able to deal with multiple deserializers trying to work on the same byte array and wanting to decrypt it while doing so, the deserializer caches the passphrase. This cache is cleared at the end of OSSL_DESERIALIZER_from_bio(). Reviewed-by: Matt Caswell Reviewed-by: Shane Lontis (Merged from https://github.com/openssl/openssl/pull/12410) --- crypto/serializer/build.info | 2 + crypto/serializer/deserializer_lib.c | 9 +- crypto/serializer/deserializer_pkey.c | 71 ++++++++++++ crypto/serializer/serdes_pass.c | 159 ++++++++++++++++++++++++++ crypto/serializer/serializer_local.h | 16 +++ crypto/serializer/serializer_pkey.c | 108 +---------------- include/openssl/core_names.h | 3 + 7 files changed, 261 insertions(+), 107 deletions(-) create mode 100644 crypto/serializer/serdes_pass.c diff --git a/crypto/serializer/build.info b/crypto/serializer/build.info index c0222785e9..11f8889b6b 100644 --- a/crypto/serializer/build.info +++ b/crypto/serializer/build.info @@ -1,3 +1,5 @@ +SOURCE[../../libcrypto]=serdes_pass.c + SOURCE[../../libcrypto]=serializer_meth.c serializer_lib.c serializer_pkey.c SOURCE[../../libcrypto]=deserializer_meth.c deserializer_lib.c \ deserializer_pkey.c diff --git a/crypto/serializer/deserializer_lib.c b/crypto/serializer/deserializer_lib.c index 2d2b1b7e7a..2fbb7782cf 100644 --- a/crypto/serializer/deserializer_lib.c +++ b/crypto/serializer/deserializer_lib.c @@ -29,12 +29,19 @@ static int deser_process(const OSSL_PARAM params[], void *arg); int OSSL_DESERIALIZER_from_bio(OSSL_DESERIALIZER_CTX *ctx, BIO *in) { struct deser_process_data_st data; + int ok = 0; memset(&data, 0, sizeof(data)); data.ctx = ctx; data.bio = in; - return deser_process(NULL, &data); + ok = deser_process(NULL, &data); + + /* Clear any cached passphrase */ + OPENSSL_clear_free(ctx->cached_passphrase, ctx->cached_passphrase_len); + ctx->cached_passphrase = NULL; + ctx->cached_passphrase_len = 0; + return ok; } #ifndef OPENSSL_NO_STDIO diff --git a/crypto/serializer/deserializer_pkey.c b/crypto/serializer/deserializer_pkey.c index 1dc35b76a7..0fafdf31aa 100644 --- a/crypto/serializer/deserializer_pkey.c +++ b/crypto/serializer/deserializer_pkey.c @@ -9,11 +9,82 @@ #include #include +#include #include +#include #include #include "crypto/evp.h" #include "serializer_local.h" +int OSSL_DESERIALIZER_CTX_set_cipher(OSSL_DESERIALIZER_CTX *ctx, + const char *cipher_name, + const char *propquery) +{ + OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END }; + + params[0] = + OSSL_PARAM_construct_utf8_string(OSSL_DESERIALIZER_PARAM_CIPHER, + (void *)cipher_name, 0); + params[1] = + OSSL_PARAM_construct_utf8_string(OSSL_DESERIALIZER_PARAM_PROPERTIES, + (void *)propquery, 0); + + return OSSL_DESERIALIZER_CTX_set_params(ctx, params); +} + +int OSSL_DESERIALIZER_CTX_set_passphrase(OSSL_DESERIALIZER_CTX *ctx, + const unsigned char *kstr, + size_t klen) +{ + OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END }; + + params[0] = OSSL_PARAM_construct_octet_string(OSSL_DESERIALIZER_PARAM_PASS, + (void *)kstr, klen); + + return OSSL_DESERIALIZER_CTX_set_params(ctx, params); +} + +static void deserializer_ctx_reset_passphrase_ui(OSSL_DESERIALIZER_CTX *ctx) +{ + UI_destroy_method(ctx->allocated_ui_method); + ctx->allocated_ui_method = NULL; + ctx->ui_method = NULL; + ctx->ui_data = NULL; +} + +int OSSL_DESERIALIZER_CTX_set_passphrase_ui(OSSL_DESERIALIZER_CTX *ctx, + const UI_METHOD *ui_method, + void *ui_data) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + deserializer_ctx_reset_passphrase_ui(ctx); + ctx->ui_method = ui_method; + ctx->ui_data = ui_data; + return 1; +} + +int OSSL_DESERIALIZER_CTX_set_passphrase_cb(OSSL_DESERIALIZER_CTX *ctx, + pem_password_cb *cb, void *cbarg) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + deserializer_ctx_reset_passphrase_ui(ctx); + if (cb == NULL) + return 1; + ctx->ui_method = + ctx->allocated_ui_method = UI_UTIL_wrap_read_pem_callback(cb, 0); + ctx->ui_data = cbarg; + + return ctx->ui_method != NULL; +} + /* * Support for OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY: * Handle an object reference diff --git a/crypto/serializer/serdes_pass.c b/crypto/serializer/serdes_pass.c new file mode 100644 index 0000000000..8a33af5e9a --- /dev/null +++ b/crypto/serializer/serdes_pass.c @@ -0,0 +1,159 @@ +/* + * Copyright 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 + */ + +#include +#include +#include +#include "internal/cryptlib.h" +#include "serializer_local.h" + +/* Passphrase callbacks for any who need it */ + +/* + * First, define the generic passphrase function that supports both + * outgoing (with passphrase verify) and incoming (without passphrase + * verify) passphrase reading. + */ +static int do_passphrase(char *pass, size_t pass_size, size_t *pass_len, + const OSSL_PARAM params[], void *arg, int verify, + const UI_METHOD *ui_method, void *ui_data, int errlib) +{ + const OSSL_PARAM *p; + const char *prompt_info = NULL; + char *prompt = NULL, *vpass = NULL; + int prompt_idx = -1, verify_idx = -1; + UI *ui = NULL; + int ret = 0; + + if (!ossl_assert(pass != NULL && pass_size != 0 && pass_len != NULL)) { + ERR_raise(errlib, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, + OSSL_PASSPHRASE_PARAM_INFO)) != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + prompt_info = p->data; + } + + if ((ui = UI_new()) == NULL) { + ERR_raise(errlib, ERR_R_MALLOC_FAILURE); + return 0; + } + + UI_set_method(ui, ui_method); + UI_add_user_data(ui, ui_data); + + /* Get an application constructed prompt */ + prompt = UI_construct_prompt(ui, "pass phrase", prompt_info); + if (prompt == NULL) { + ERR_raise(errlib, ERR_R_MALLOC_FAILURE); + goto end; + } + + prompt_idx = UI_add_input_string(ui, prompt, + UI_INPUT_FLAG_DEFAULT_PWD, + pass, 0, pass_size - 1) - 1; + if (prompt_idx < 0) { + ERR_raise(errlib, ERR_R_UI_LIB); + goto end; + } + + if (verify) { + /* Get a buffer for verification prompt */ + vpass = OPENSSL_zalloc(pass_size); + if (vpass == NULL) { + ERR_raise(errlib, ERR_R_MALLOC_FAILURE); + goto end; + } + verify_idx = UI_add_verify_string(ui, prompt, + UI_INPUT_FLAG_DEFAULT_PWD, + vpass, 0, pass_size - 1, + pass) - 1; + if (verify_idx < 0) { + ERR_raise(errlib, ERR_R_UI_LIB); + goto end; + } + } + + switch (UI_process(ui)) { + case -2: + ERR_raise(errlib, ERR_R_INTERRUPTED_OR_CANCELLED); + break; + case -1: + ERR_raise(errlib, ERR_R_UI_LIB); + break; + default: + *pass_len = (size_t)UI_get_result_length(ui, prompt_idx); + ret = 1; + break; + } + + end: + OPENSSL_free(vpass); + OPENSSL_free(prompt); + UI_free(ui); + return ret; +} + +/* + * Serializers typically want to get an outgoing passphrase, while + * deserializers typically want to get en incoming passphrase. + */ +int ossl_serializer_passphrase_out_cb(char *pass, size_t pass_size, + size_t *pass_len, + const OSSL_PARAM params[], void *arg) +{ + OSSL_SERIALIZER_CTX *ctx = arg; + + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + return do_passphrase(pass, pass_size, pass_len, params, arg, 1, + ctx->ui_method, ctx->ui_data, + ERR_LIB_OSSL_SERIALIZER); +} + +int ossl_deserializer_passphrase_in_cb(char *pass, size_t pass_size, + size_t *pass_len, + const OSSL_PARAM params[], void *arg) +{ + OSSL_DESERIALIZER_CTX *ctx = arg; + + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (ctx->cached_passphrase != NULL) { + size_t len = ctx->cached_passphrase_len; + + if (len > pass_size) + len = pass_size; + memcpy(pass, ctx->cached_passphrase, len); + *pass_len = len; + return 1; + } else { + if ((ctx->cached_passphrase = OPENSSL_zalloc(pass_size)) == NULL) { + ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_MALLOC_FAILURE); + return 0; + } + } + if (do_passphrase(pass, pass_size, pass_len, params, arg, 0, + ctx->ui_method, ctx->ui_data, + ERR_LIB_OSSL_DESERIALIZER)) { + memcpy(ctx->cached_passphrase, pass, *pass_len); + ctx->cached_passphrase_len = *pass_len; + return 1; + } + return 0; +} diff --git a/crypto/serializer/serializer_local.h b/crypto/serializer/serializer_local.h index 46ff84cc7a..acf600c285 100644 --- a/crypto/serializer/serializer_local.h +++ b/crypto/serializer/serializer_local.h @@ -110,4 +110,20 @@ struct ossl_deserializer_ctx_st { * intermediary storage. */ UI_METHOD *allocated_ui_method; + /* + * Because the same input may pass through more than one deserializer, + * we cache any passphrase passed to us. The desrializing processor + * must clear this at the end of a run. + */ + unsigned char *cached_passphrase; + size_t cached_passphrase_len; }; + +/* Passphrase callbacks, found in serdes_pass.c */ + +/* + * Serializers typically want to get an outgoing passphrase, while + * deserializers typically want to get en incoming passphrase. + */ +OSSL_PASSPHRASE_CALLBACK ossl_serializer_passphrase_out_cb; +OSSL_PASSPHRASE_CALLBACK ossl_deserializer_passphrase_in_cb; diff --git a/crypto/serializer/serializer_pkey.c b/crypto/serializer/serializer_pkey.c index 35ddb92bd4..6e24ed73f0 100644 --- a/crypto/serializer/serializer_pkey.c +++ b/crypto/serializer/serializer_pkey.c @@ -107,110 +107,6 @@ static void cache_serializers(const char *name, void *data) d->error = 1; } -/* - * Support for OSSL_SERIALIZER_CTX_new_by_TYPE and OSSL_SERIALIZER_to_bio: - * Passphrase callbacks - */ - -/* - * First, we define the generic passphrase function that supports both - * outgoing (with passphrase verify) and incoming (without passphrase verify) - * passphrase reading. - */ -static int serializer_passphrase(char *pass, size_t pass_size, - size_t *pass_len, int verify, - const OSSL_PARAM params[], void *arg) -{ - OSSL_SERIALIZER_CTX *ctx = arg; - const OSSL_PARAM *p; - const char *prompt_info = NULL; - char *prompt = NULL, *vpass = NULL; - int prompt_idx = -1, verify_idx = -1; - UI *ui = NULL; - int ret = 0; - - if (!ossl_assert(ctx != NULL && pass != NULL - && pass_size != 0 && pass_len != NULL)) { - ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER); - return 0; - } - - if ((p = OSSL_PARAM_locate_const(params, - OSSL_PASSPHRASE_PARAM_INFO)) != NULL) { - if (p->data_type != OSSL_PARAM_UTF8_STRING) - return 0; - prompt_info = p->data; - } - - if ((ui = UI_new()) == NULL) { - ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE); - return 0; - } - - UI_set_method(ui, ctx->ui_method); - UI_add_user_data(ui, ctx->ui_data); - - /* Get an application constructed prompt */ - prompt = UI_construct_prompt(ui, "pass phrase", prompt_info); - if (prompt == NULL) { - ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE); - goto end; - } - - prompt_idx = UI_add_input_string(ui, prompt, - UI_INPUT_FLAG_DEFAULT_PWD, - pass, 0, pass_size - 1) - 1; - if (prompt_idx < 0) { - ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB); - goto end; - } - - if (verify) { - /* Get a buffer for verification prompt */ - vpass = OPENSSL_zalloc(pass_size); - if (vpass == NULL) { - ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE); - goto end; - } - verify_idx = UI_add_verify_string(ui, prompt, - UI_INPUT_FLAG_DEFAULT_PWD, - vpass, 0, pass_size - 1, - pass) - 1; - if (verify_idx < 0) { - ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB); - goto end; - } - } - - switch (UI_process(ui)) { - case -2: - ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_INTERRUPTED_OR_CANCELLED); - break; - case -1: - ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB); - break; - default: - *pass_len = (size_t)UI_get_result_length(ui, prompt_idx); - ret = 1; - break; - } - - end: - OPENSSL_free(vpass); - OPENSSL_free(prompt); - UI_free(ui); - return ret; -} - -/* Ensure correct function definition for outgoing passphrase reader */ -static OSSL_PASSPHRASE_CALLBACK serializer_passphrase_out_cb; -static int serializer_passphrase_out_cb(char *pass, size_t pass_size, - size_t *pass_len, - const OSSL_PARAM params[], void *arg) -{ - return serializer_passphrase(pass, pass_size, pass_len, 1, params, arg); -} - /* * Support for OSSL_SERIALIZER_to_bio: * writing callback for the OSSL_PARAM (the implementation doesn't have @@ -229,7 +125,7 @@ static int serializer_write_cb(const OSSL_PARAM params[], void *arg) BIO *out = write_data->out; return ctx->ser->serialize_data(ctx->serctx, params, (OSSL_CORE_BIO *)out, - serializer_passphrase_out_cb, ctx); + ossl_serializer_passphrase_out_cb, ctx); } /* @@ -266,7 +162,7 @@ static int serializer_EVP_PKEY_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out) return ctx->ser->serialize_object(ctx->serctx, keydata, (OSSL_CORE_BIO *)out, - serializer_passphrase_out_cb, ctx); + ossl_serializer_passphrase_out_cb, ctx); } /* diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h index c380b45fbb..9ce4115a89 100644 --- a/include/openssl/core_names.h +++ b/include/openssl/core_names.h @@ -404,6 +404,9 @@ extern "C" { #define OSSL_SERIALIZER_PARAM_PROPERTIES OSSL_ALG_PARAM_PROPERTIES #define OSSL_SERIALIZER_PARAM_PASS "passphrase" +#define OSSL_DESERIALIZER_PARAM_CIPHER OSSL_ALG_PARAM_CIPHER +#define OSSL_DESERIALIZER_PARAM_PROPERTIES OSSL_ALG_PARAM_PROPERTIES +#define OSSL_DESERIALIZER_PARAM_PASS "passphrase" #define OSSL_DESERIALIZER_PARAM_INPUT_TYPE "input-type" #define OSSL_DESERIALIZER_PARAM_DATA_TYPE "data-type" #define OSSL_DESERIALIZER_PARAM_DATA "data" -- 2.34.1