mirror of
https://github.com/openssl/openssl.git
synced 2025-02-11 14:22:43 +08:00
The pass phrase prompter that's part of OSSL_ENCODER and OSSL_DECODER is really a passphrase callback bridge between the diverse forms of prompters that exist within OpenSSL: pem_password_cb, ui_method and OSSL_PASSPHRASE_CALLBACK. This can be generalised, to be re-used by other parts of OpenSSL, and to thereby allow the users to specify whatever form of pass phrase callback they need, while being able to pass that on to other APIs that are called internally, in the form that those APIs demand. Additionally, we throw in the possibility to cache pass phrases during a "session" (we leave it to each API to define what a "session" is). This is useful for any API that implements discovery and therefore may need to get the same password more than once, such as OSSL_DECODER and OSSL_STORE. Reviewed-by: Shane Lontis <shane.lontis@oracle.com> (Merged from https://github.com/openssl/openssl/pull/12512)
251 lines
8.5 KiB
C
251 lines
8.5 KiB
C
/*
|
|
* Copyright 2019-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 <openssl/err.h>
|
|
#include <openssl/ui.h>
|
|
#include <openssl/params.h>
|
|
#include <openssl/encoder.h>
|
|
#include <openssl/core_names.h>
|
|
#include <openssl/safestack.h>
|
|
#include "internal/provider.h"
|
|
#include "internal/property.h"
|
|
#include "crypto/evp.h"
|
|
#include "encoder_local.h"
|
|
|
|
DEFINE_STACK_OF_CSTRING()
|
|
|
|
int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_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_ENCODER_PARAM_CIPHER,
|
|
(void *)cipher_name, 0);
|
|
params[1] =
|
|
OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_PROPERTIES,
|
|
(void *)propquery, 0);
|
|
|
|
return OSSL_ENCODER_CTX_set_params(ctx, params);
|
|
}
|
|
|
|
int OSSL_ENCODER_CTX_set_passphrase(OSSL_ENCODER_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_ENCODER_PARAM_PASS,
|
|
(void *)kstr, klen);
|
|
|
|
return OSSL_ENCODER_CTX_set_params(ctx, params);
|
|
}
|
|
|
|
int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx,
|
|
const UI_METHOD *ui_method,
|
|
void *ui_data)
|
|
{
|
|
return ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data);
|
|
}
|
|
|
|
int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
|
|
pem_password_cb *cb, void *cbarg)
|
|
{
|
|
return ossl_pw_set_pem_password_cb(&ctx->pwdata, cb, cbarg);
|
|
}
|
|
|
|
/*
|
|
* Support for OSSL_ENCODER_CTX_new_by_TYPE:
|
|
* finding a suitable encoder
|
|
*/
|
|
|
|
struct selected_encoder_st {
|
|
STACK_OF(OPENSSL_CSTRING) *names;
|
|
int error;
|
|
};
|
|
|
|
static void cache_encoders(const char *name, void *data)
|
|
{
|
|
struct selected_encoder_st *d = data;
|
|
|
|
if (sk_OPENSSL_CSTRING_push(d->names, name) <= 0)
|
|
d->error = 1;
|
|
}
|
|
|
|
/*
|
|
* Support for OSSL_ENCODER_to_bio:
|
|
* writing callback for the OSSL_PARAM (the implementation doesn't have
|
|
* intimate knowledge of the provider side object)
|
|
*/
|
|
|
|
struct encoder_write_data_st {
|
|
OSSL_ENCODER_CTX *ctx;
|
|
BIO *out;
|
|
};
|
|
|
|
static int encoder_write_cb(const OSSL_PARAM params[], void *arg)
|
|
{
|
|
struct encoder_write_data_st *write_data = arg;
|
|
OSSL_ENCODER_CTX *ctx = write_data->ctx;
|
|
BIO *out = write_data->out;
|
|
|
|
return ctx->encoder->encode_data(ctx->serctx, params, (OSSL_CORE_BIO *)out,
|
|
ossl_pw_passphrase_callback_enc,
|
|
&ctx->pwdata);
|
|
}
|
|
|
|
/*
|
|
* Support for OSSL_ENCODER_to_bio:
|
|
* Perform the actual output.
|
|
*/
|
|
|
|
static int encoder_EVP_PKEY_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out)
|
|
{
|
|
const EVP_PKEY *pkey = ctx->object;
|
|
void *keydata = pkey->keydata;
|
|
EVP_KEYMGMT *keymgmt = pkey->keymgmt;
|
|
|
|
/*
|
|
* OSSL_ENCODER_CTX_new() creates a context, even when the
|
|
* encoder it's given is NULL. Callers can detect the lack
|
|
* of encoder with OSSL_ENCODER_CTX_get_encoder() and
|
|
* should take precautions, possibly call a fallback instead of
|
|
* OSSL_ENCODER_to_bio() / OSSL_ENCODER_to_fp(). If it's
|
|
* come this far, we return an error.
|
|
*/
|
|
if (ctx->encoder == NULL)
|
|
return 0;
|
|
|
|
if (ctx->encoder->encode_object == NULL
|
|
|| (OSSL_ENCODER_provider(ctx->encoder)
|
|
!= EVP_KEYMGMT_provider(keymgmt))) {
|
|
struct encoder_write_data_st write_data;
|
|
|
|
write_data.ctx = ctx;
|
|
write_data.out = out;
|
|
|
|
return evp_keymgmt_export(keymgmt, keydata, ctx->selection,
|
|
&encoder_write_cb, &write_data);
|
|
}
|
|
|
|
return ctx->encoder->encode_object(ctx->serctx, keydata,
|
|
(OSSL_CORE_BIO *)out,
|
|
ossl_pw_passphrase_callback_enc,
|
|
&ctx->pwdata);
|
|
}
|
|
|
|
/*
|
|
* OSSL_ENCODER_CTX_new_by_EVP_PKEY() returns a ctx with no encoder if
|
|
* it couldn't find a suitable encoder. This allows a caller to detect if
|
|
* a suitable encoder was found, with OSSL_ENCODER_CTX_get_encoder(),
|
|
* and to use fallback methods if the result is NULL.
|
|
*/
|
|
OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
|
|
const char *propquery)
|
|
{
|
|
OSSL_ENCODER_CTX *ctx = NULL;
|
|
OSSL_ENCODER *encoder = NULL;
|
|
EVP_KEYMGMT *keymgmt = pkey->keymgmt;
|
|
int selection = OSSL_KEYMGMT_SELECT_ALL;
|
|
|
|
if (!ossl_assert(pkey != NULL && propquery != NULL)) {
|
|
ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
if (keymgmt != NULL) {
|
|
const OSSL_PROVIDER *desired_prov = EVP_KEYMGMT_provider(keymgmt);
|
|
OPENSSL_CTX *libctx = ossl_provider_library_context(desired_prov);
|
|
struct selected_encoder_st sel_data;
|
|
OSSL_ENCODER *first = NULL;
|
|
const char *name;
|
|
int i;
|
|
|
|
/*
|
|
* Select the encoder in two steps. First, get the names of all of
|
|
* the encoders. Then determine which is the best one to use.
|
|
* This has to be broken because it isn't possible to fetch the
|
|
* serialisers inside EVP_KEYMGMT_names_do_all() due to locking
|
|
* order inversions with the store lock.
|
|
*/
|
|
sel_data.error = 0;
|
|
sel_data.names = sk_OPENSSL_CSTRING_new_null();
|
|
if (sel_data.names == NULL)
|
|
return NULL;
|
|
EVP_KEYMGMT_names_do_all(keymgmt, cache_encoders, &sel_data);
|
|
/*
|
|
* Ignore memory allocation errors that are indicated in sel_data.error
|
|
* in case a suitable provider does get found regardless.
|
|
*/
|
|
|
|
/*
|
|
* Encoders offer two functions, one that handles object data in
|
|
* the form of a OSSL_PARAM array, and one that directly handles a
|
|
* provider side object. The latter requires that the encoder
|
|
* is offered by the same provider that holds that object, but is
|
|
* more desirable because it usually provides faster encoding.
|
|
*
|
|
* When looking up possible encoders, we save the first that can
|
|
* handle an OSSL_PARAM array in |first| and use that if nothing
|
|
* better turns up.
|
|
*/
|
|
for (i = 0; i < sk_OPENSSL_CSTRING_num(sel_data.names); i++) {
|
|
name = sk_OPENSSL_CSTRING_value(sel_data.names, i);
|
|
encoder = OSSL_ENCODER_fetch(libctx, name, propquery);
|
|
if (encoder != NULL) {
|
|
if (OSSL_ENCODER_provider(encoder) == desired_prov
|
|
&& encoder->encode_object != NULL) {
|
|
OSSL_ENCODER_free(first);
|
|
break;
|
|
}
|
|
if (first == NULL && encoder->encode_data != NULL)
|
|
first = encoder;
|
|
else
|
|
OSSL_ENCODER_free(encoder);
|
|
encoder = NULL;
|
|
}
|
|
}
|
|
sk_OPENSSL_CSTRING_free(sel_data.names);
|
|
if (encoder == NULL)
|
|
encoder = first;
|
|
|
|
if (encoder != NULL) {
|
|
OSSL_PROPERTY_LIST *check = NULL, *current_props = NULL;
|
|
|
|
check = ossl_parse_query(libctx, "type=parameters");
|
|
current_props =
|
|
ossl_parse_property(libctx, OSSL_ENCODER_properties(encoder));
|
|
if (ossl_property_match_count(check, current_props) > 0)
|
|
selection = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS;
|
|
ossl_property_free(current_props);
|
|
ossl_property_free(check);
|
|
} else {
|
|
if (sel_data.error)
|
|
ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
|
|
else
|
|
ERR_raise(ERR_LIB_OSSL_ENCODER,
|
|
OSSL_ENCODER_R_ENCODER_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
ctx = OSSL_ENCODER_CTX_new(encoder); /* refcnt(encoder)++ */
|
|
OSSL_ENCODER_free(encoder); /* refcnt(encoder)-- */
|
|
|
|
if (ctx != NULL) {
|
|
/* Setup for OSSL_ENCODE_to_bio() */
|
|
ctx->selection = selection;
|
|
ctx->object = pkey;
|
|
ctx->do_output = encoder_EVP_PKEY_to_bio;
|
|
}
|
|
|
|
return ctx;
|
|
}
|
|
|