The context builds on CRYPTO_EX_DATA, allowing it to be dynamically extended with new data from the different parts of libcrypto. Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/8225)master
@ -0,0 +1,110 @@ | |||
/* | |||
* 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 "internal/cryptlib.h" | |||
#include "internal/thread_once.h" | |||
struct openssl_ctx_st { | |||
CRYPTO_RWLOCK *lock; | |||
CRYPTO_EX_DATA data; | |||
}; | |||
static OPENSSL_CTX default_context; | |||
static int context_init(OPENSSL_CTX *ctx) | |||
{ | |||
return (ctx->lock = CRYPTO_THREAD_lock_new()) != NULL | |||
&& CRYPTO_new_ex_data(CRYPTO_EX_INDEX_OPENSSL_CTX, NULL, | |||
&ctx->data); | |||
} | |||
static int context_deinit(OPENSSL_CTX *ctx) | |||
{ | |||
CRYPTO_free_ex_data(CRYPTO_EX_INDEX_OPENSSL_CTX, NULL, &ctx->data); | |||
CRYPTO_THREAD_lock_free(ctx->lock); | |||
return 1; | |||
} | |||
static CRYPTO_ONCE default_context_init = CRYPTO_ONCE_STATIC_INIT; | |||
static void do_default_context_deinit(void) | |||
{ | |||
context_deinit(&default_context); | |||
} | |||
DEFINE_RUN_ONCE_STATIC(do_default_context_init) | |||
{ | |||
return OPENSSL_init_crypto(0, NULL) | |||
&& context_init(&default_context) | |||
&& OPENSSL_atexit(do_default_context_deinit); | |||
} | |||
OPENSSL_CTX *OPENSSL_CTX_new(void) | |||
{ | |||
OPENSSL_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); | |||
if (ctx != NULL && !context_init(ctx)) { | |||
OPENSSL_CTX_free(ctx); | |||
ctx = NULL; | |||
} | |||
return ctx; | |||
} | |||
void OPENSSL_CTX_free(OPENSSL_CTX *ctx) | |||
{ | |||
if (ctx != NULL) | |||
context_deinit(ctx); | |||
OPENSSL_free(ctx); | |||
} | |||
static void openssl_ctx_generic_new(void *parent_ign, void *ptr_ign, | |||
CRYPTO_EX_DATA *ad, int index, | |||
long argl_ign, void *argp) | |||
{ | |||
const OPENSSL_CTX_METHOD *meth = argp; | |||
void *ptr = meth->new_func(); | |||
if (ptr != NULL) | |||
CRYPTO_set_ex_data(ad, index, ptr); | |||
} | |||
static void openssl_ctx_generic_free(void *parent_ign, void *ptr, | |||
CRYPTO_EX_DATA *ad, int index, | |||
long argl_ign, void *argp) | |||
{ | |||
const OPENSSL_CTX_METHOD *meth = argp; | |||
meth->free_func(ptr); | |||
} | |||
int openssl_ctx_new_index(const OPENSSL_CTX_METHOD *meth) | |||
{ | |||
return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_OPENSSL_CTX, 0, (void *)meth, | |||
openssl_ctx_generic_new, NULL, | |||
openssl_ctx_generic_free); | |||
} | |||
void *openssl_ctx_get_data(OPENSSL_CTX *ctx, int index) | |||
{ | |||
void *data = NULL; | |||
if (ctx == NULL) { | |||
if (!RUN_ONCE(&default_context_init, do_default_context_init)) | |||
return 0; | |||
ctx = &default_context; | |||
} | |||
CRYPTO_THREAD_read_lock(ctx->lock); | |||
/* The alloc call ensures there's a value there */ | |||
if (CRYPTO_alloc_ex_data(CRYPTO_EX_INDEX_OPENSSL_CTX, NULL, | |||
&ctx->data, index)) | |||
data = CRYPTO_get_ex_data(&ctx->data, index); | |||
CRYPTO_THREAD_unlock(ctx->lock); | |||
return data; | |||
} | |||
@ -0,0 +1,117 @@ | |||
=pod | |||
=head1 NAME | |||
openssl_ctx_new_index, openssl_ctx_free_index, | |||
openssl_ctx_new_fn, openssl_ctx_free_fn, | |||
openssl_ctx_set_data, openssl_ctx_get_data - internal OPENSSL_CTX routines | |||
=head1 SYNOPSIS | |||
#include <openssl/ossl_typ.h> | |||
#include "internal/cryptlib.h" | |||
typedef CRYPTO_EX_new openssl_ctx_new_fn; | |||
typedef CRYPTO_EX_free openssl_ctx_free_fn; | |||
typedef struct openssl_ctx_method { | |||
void *(*new_func)(void); | |||
void (*free_func)(void *); | |||
} OPENSSL_CTX_METHOD; | |||
int openssl_ctx_new_index(const OPENSSL_CTX_METHOD *meth); | |||
void *openssl_ctx_get_data(OPENSSL_CTX *ctx, int index); | |||
=head1 DESCRIPTION | |||
Internally, the OpenSSL library context C<OPENSSL_CTX> is implemented | |||
as a C<CRYPTO_EX_DATA>, which allows data from diverse parts of the | |||
library to be added and removed dynamically. | |||
Each such data item must have a corresponding CRYPTO_EX_DATA index | |||
associated with it. | |||
See the example further down to see how that's done. | |||
openssl_ctx_new_index() allocates a new library context index, and | |||
associates it with the functions given through C<meth>. | |||
The functions given through that method are used to create or free | |||
items that are stored at that index whenever a library context is | |||
created or freed, meaning that the code that use a data item of that | |||
index doesn't have to worry about that, just use the data available. | |||
Deallocation of an index happens automatically when the library | |||
context is freed. | |||
openssl_ctx_get_data() is used to retrieve a pointer to the data in | |||
the library context C<ctx> associated with the given C<index>. | |||
=head1 EXAMPLES | |||
=head2 Initialization | |||
For a type C<FOO> that should end up in the OpenSSL library context, a | |||
small bit of initialization is needed, i.e. to associate a constructor | |||
and a destructor to a new index. | |||
/* The index will always be entirely global, and dynamically allocated */ | |||
static int foo_index = -1; | |||
typedef struct foo_st { | |||
int i; | |||
void *data; | |||
} FOO; | |||
static void *foo_new(void) | |||
{ | |||
FOO *ptr = OPENSSL_zalloc(sizeof(*foo)); | |||
if (ptr != NULL) | |||
ptr->i = 42; | |||
return ptr; | |||
} | |||
static void foo_free(void *ptr) | |||
{ | |||
OPENSSL_free(ptr); | |||
} | |||
static const OPENSSL_CTX_METHOD foo_method = { | |||
foo_new, | |||
foo_free | |||
}; | |||
static int foo_init(void) | |||
{ | |||
foo_index = openssl_ctx_new_index(foo_method); | |||
return foo_index != -1; | |||
} | |||
=head2 Usage | |||
To get and use the data stored in the library context, simply do this: | |||
/* | |||
* ctx is received from a caller, | |||
* foo_index comes from the example above | |||
*/ | |||
FOO *data = openssl_ctx_get_data(ctx, foo_index); | |||
=head1 RETURN VALUES | |||
openssl_ctx_new_index() returns -1 on error, otherwise the allocated | |||
index number. | |||
openssl_ctx_get_data() returns a pointer on success, or C<NULL> on | |||
failure. | |||
=head1 SEE ALSO | |||
L<OPENSSL_CTX(3)> | |||
=head1 COPYRIGHT | |||
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 | |||
L<https://www.openssl.org/source/license.html>. | |||
=cut |
@ -0,0 +1,48 @@ | |||
=pod | |||
=head1 NAME | |||
OPENSSL_CTX, OPENSSL_CTX_new, OPENSSL_CTX_free - OpenSSL library context | |||
=head1 SYNOPSIS | |||
#include <openssl/crypto.h> | |||
typedef struct openssl_ctx_st OPENSSL_CTX; | |||
OPENSSL_CTX *OPENSSL_CTX_new(void); | |||
void OPENSSL_CTX_free(OPENSSL_CTX *ctx); | |||
=head1 DESCRIPTION | |||
C<OPENSSL_CTX> is an internal OpenSSL library context type. | |||
Applications may allocate their own, but may also use C<NULL> to use | |||
the internal default context with functions that take a C<OPENSSL_CTX> | |||
argument. | |||
OPENSSL_CTX_new() creates a new OpenSSL library context. | |||
OPENSSL_CTX_free() frees the given C<ctx>. | |||
=head1 RETURN VALUES | |||
OPENSSL_CTX_new() return a library context pointer on success, or | |||
C<NULL> on error. | |||
OPENSSL_CTX_free() doesn't return any value. | |||
=head1 HISTORY | |||
OPENSSL_CTX, OPENSSL_CTX_new() and OPENSSL_CTX_free() | |||
were added in OpenSSL 3.0.0. | |||
=head1 COPYRIGHT | |||
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 | |||
L<https://www.openssl.org/source/license.html>. | |||
=cut |
@ -0,0 +1,89 @@ | |||
/* | |||
* 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 | |||
*/ | |||
/* Internal tests for the OpenSSL library context */ | |||
#include "internal/cryptlib.h" | |||
#include "testutil.h" | |||
/* | |||
* Everything between BEGIN EXAMPLE and END EXAMPLE is copied from | |||
* doc/internal/man3/openssl_ctx_get_data.pod | |||
*/ | |||
/* | |||
* ====================================================================== | |||
* BEGIN EXAMPLE | |||
*/ | |||
/* The index will always be entirely global, and dynamically allocated */ | |||
static int foo_index = -1; | |||
typedef struct foo_st { | |||
int i; | |||
void *data; | |||
} FOO; | |||
static void *foo_new(void) | |||
{ | |||
FOO *ptr = OPENSSL_zalloc(sizeof(*ptr)); | |||
if (ptr != NULL) | |||
ptr->i = 42; | |||
return ptr; | |||
} | |||
static void foo_free(void *ptr) | |||
{ | |||
OPENSSL_free(ptr); | |||
} | |||
static const OPENSSL_CTX_METHOD foo_method = { | |||
foo_new, | |||
foo_free | |||
}; | |||
static int foo_init(void) { | |||
foo_index = openssl_ctx_new_index(&foo_method); | |||
return foo_index != -1; | |||
} | |||
/* | |||
* END EXAMPLE | |||
* ====================================================================== | |||
*/ | |||
static int test_context(OPENSSL_CTX *ctx) | |||
{ | |||
FOO *data = NULL; | |||
return (TEST_ptr(data = openssl_ctx_get_data(ctx, foo_index)) | |||
/* OPENSSL_zalloc in foo_new() initialized it to zero */ | |||
&& TEST_int_eq(data->i, 42)); | |||
} | |||
static int test_app_context(void) | |||
{ | |||
OPENSSL_CTX *ctx = NULL; | |||
int result = (TEST_ptr(ctx = OPENSSL_CTX_new()) && test_context(ctx)); | |||
OPENSSL_CTX_free(ctx); | |||
return result; | |||
} | |||
static int test_def_context(void) | |||
{ | |||
return test_context(NULL); | |||
} | |||
int setup_tests(void) | |||
{ | |||
ADD_TEST(foo_init); | |||
ADD_TEST(test_app_context); | |||
ADD_TEST(test_def_context); | |||
return 1; | |||
} |
@ -0,0 +1,16 @@ | |||
#! /usr/bin/env perl | |||
# 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 | |||
use strict; | |||
use OpenSSL::Test; # get 'plan' | |||
use OpenSSL::Test::Simple; | |||
use OpenSSL::Test::Utils; | |||
setup("test_internal_context"); | |||
simple_test("test_internal_context", "context_internal_test"); |