Introduce OpenSSL INI for selecting libctx (#18768)

Closes GH-18768

Co-authored-by: Gina Peter Banyard <girgias@php.net>
This commit is contained in:
Jakub Zelenka 2025-07-14 14:16:14 +01:00 committed by GitHub
parent 2beb44a80b
commit d0c0a9abfd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 95 additions and 32 deletions

View file

@ -351,12 +351,31 @@ int php_openssl_get_ssl_stream_data_index(void)
return ssl_stream_data_index; return ssl_stream_data_index;
} }
/* {{{ INI Settings */ static PHP_INI_MH(OnUpdateLibCtx)
{
#if PHP_OPENSSL_API_VERSION >= 0x30000
if (zend_string_equals_literal(new_value, "default")) {
OPENSSL_G(ctx).libctx = OPENSSL_G(ctx).default_libctx;
} else if (zend_string_equals_literal(new_value, "custom")) {
OPENSSL_G(ctx).libctx = OPENSSL_G(ctx).custom_libctx;
} else {
/* Do not output error when restoring ini options. */
if (stage != ZEND_INI_STAGE_DEACTIVATE) {
int err_type = stage == ZEND_INI_STAGE_RUNTIME ? E_WARNING : E_ERROR;
php_error_docref(NULL, err_type, "OpenSSL libctx \"%s\" cannot be found", ZSTR_VAL(new_value));
}
return FAILURE;
}
#endif
return SUCCESS;
}
PHP_INI_BEGIN() PHP_INI_BEGIN()
PHP_INI_ENTRY("openssl.cafile", NULL, PHP_INI_PERDIR, NULL) PHP_INI_ENTRY("openssl.cafile", NULL, PHP_INI_PERDIR, NULL)
PHP_INI_ENTRY("openssl.capath", NULL, PHP_INI_PERDIR, NULL) PHP_INI_ENTRY("openssl.capath", NULL, PHP_INI_PERDIR, NULL)
PHP_INI_ENTRY("openssl.libctx", "custom", PHP_INI_PERDIR, OnUpdateLibCtx)
PHP_INI_END() PHP_INI_END()
/* }}} */
/* {{{ PHP_MINIT_FUNCTION */ /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(openssl) PHP_MINIT_FUNCTION(openssl)
@ -438,9 +457,7 @@ PHP_GINIT_FUNCTION(openssl)
#endif #endif
openssl_globals->errors = NULL; openssl_globals->errors = NULL;
openssl_globals->errors_mark = NULL; openssl_globals->errors_mark = NULL;
#if PHP_OPENSSL_API_VERSION >= 0x30000 php_openssl_backend_init_libctx(&openssl_globals->ctx);
php_openssl_backend_init_libctx(&openssl_globals->libctx, &openssl_globals->propq);
#endif
} }
/* }}} */ /* }}} */
@ -453,9 +470,7 @@ PHP_GSHUTDOWN_FUNCTION(openssl)
if (openssl_globals->errors_mark) { if (openssl_globals->errors_mark) {
pefree(openssl_globals->errors_mark, 1); pefree(openssl_globals->errors_mark, 1);
} }
#if PHP_OPENSSL_API_VERSION >= 0x30000 php_openssl_backend_destroy_libctx(&openssl_globals->ctx);
php_openssl_backend_destroy_libctx(openssl_globals->libctx, openssl_globals->propq);
#endif
} }
/* }}} */ /* }}} */

View file

@ -44,6 +44,16 @@ void php_openssl_backend_shutdown(void)
#endif #endif
} }
void php_openssl_backend_init_libctx(struct php_openssl_libctx *ctx)
{
// Do nothing as there is no libctx
}
void php_openssl_backend_destroy_libctx(struct php_openssl_libctx *ctx)
{
// Do nothing as there is no libctx
}
EVP_PKEY_CTX *php_openssl_pkey_new_from_name(const char *name, int id) EVP_PKEY_CTX *php_openssl_pkey_new_from_name(const char *name, int id)
{ {
return EVP_PKEY_CTX_new_id(id, NULL); return EVP_PKEY_CTX_new_id(id, NULL);

View file

@ -29,32 +29,47 @@ void php_openssl_backend_shutdown(void)
(void) 0; (void) 0;
} }
void php_openssl_backend_init_libctx(OSSL_LIB_CTX **plibctx, char **ppropq) #define PHP_OPENSSL_DEFAULT_CONF_MFLAGS \
(CONF_MFLAGS_DEFAULT_SECTION | CONF_MFLAGS_IGNORE_MISSING_FILE | CONF_MFLAGS_IGNORE_RETURN_CODES)
void php_openssl_backend_init_libctx(struct php_openssl_libctx *ctx)
{ {
/* The return value is not checked because we cannot reasonable fail in GINIT so using NULL ctx->default_libctx = OSSL_LIB_CTX_get0_global_default();
* (default context) is probably better. */ ctx->custom_libctx = OSSL_LIB_CTX_new();
*plibctx = OSSL_LIB_CTX_new(); if (ctx->custom_libctx != NULL) {
*ppropq = NULL; /* This is not being checked because there is not much that can be done. */
CONF_modules_load_file_ex(ctx->custom_libctx, NULL, NULL,
PHP_OPENSSL_DEFAULT_CONF_MFLAGS);
#ifdef LOAD_OPENSSL_LEGACY_PROVIDER
OSSL_PROVIDER_load(ctx->custom_libctx, "legacy");
OSSL_PROVIDER_load(ctx->custom_libctx, "default");
#endif
ctx->libctx = ctx->custom_libctx;
} else {
/* If creation fails, just fallback to default */
ctx->libctx = ctx->default_libctx;
}
ctx->propq = NULL;
} }
void php_openssl_backend_destroy_libctx(OSSL_LIB_CTX *libctx, char *propq) void php_openssl_backend_destroy_libctx(struct php_openssl_libctx *ctx)
{ {
if (libctx != NULL) { if (ctx->custom_libctx != NULL) {
OSSL_LIB_CTX_free(libctx); OSSL_LIB_CTX_free(ctx->custom_libctx);
} }
if (propq != NULL) { if (ctx->propq != NULL) {
free(propq); free(ctx->propq);
} }
} }
EVP_PKEY_CTX *php_openssl_pkey_new_from_name(const char *name, int id) EVP_PKEY_CTX *php_openssl_pkey_new_from_name(const char *name, int id)
{ {
return EVP_PKEY_CTX_new_from_name(OPENSSL_G(libctx), name, OPENSSL_G(propq)); return EVP_PKEY_CTX_new_from_name(PHP_OPENSSL_LIBCTX, name, PHP_OPENSSL_PROPQ);
} }
EVP_PKEY_CTX *php_openssl_pkey_new_from_pkey(EVP_PKEY *pkey) EVP_PKEY_CTX *php_openssl_pkey_new_from_pkey(EVP_PKEY *pkey)
{ {
return EVP_PKEY_CTX_new_from_pkey(OPENSSL_G(libctx), pkey, OPENSSL_G(propq)); return EVP_PKEY_CTX_new_from_pkey(PHP_OPENSSL_LIBCTX, pkey, PHP_OPENSSL_PROPQ);
} }
EVP_PKEY *php_openssl_pkey_init_rsa(zval *data) EVP_PKEY *php_openssl_pkey_init_rsa(zval *data)
@ -299,7 +314,7 @@ EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) {
goto cleanup; goto cleanup;
} }
if (!(group = EC_GROUP_new_by_curve_name_ex(OPENSSL_G(libctx), OPENSSL_G(propq), nid))) { if (!(group = EC_GROUP_new_by_curve_name_ex(PHP_OPENSSL_LIBCTX, PHP_OPENSSL_PROPQ, nid))) {
goto cleanup; goto cleanup;
} }
@ -698,7 +713,7 @@ zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pu
const EVP_MD *php_openssl_get_evp_md_by_name(const char *name) const EVP_MD *php_openssl_get_evp_md_by_name(const char *name)
{ {
return EVP_MD_fetch(OPENSSL_G(libctx), name, OPENSSL_G(propq)); return EVP_MD_fetch(PHP_OPENSSL_LIBCTX, name, PHP_OPENSSL_PROPQ);
} }
static const char *php_openssl_digest_names[] = { static const char *php_openssl_digest_names[] = {
@ -754,7 +769,7 @@ static const char *php_openssl_cipher_names[] = {
const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *name) const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *name)
{ {
return EVP_CIPHER_fetch(OPENSSL_G(libctx), name, OPENSSL_G(propq)); return EVP_CIPHER_fetch(PHP_OPENSSL_LIBCTX, name, PHP_OPENSSL_PROPQ);
} }
const EVP_CIPHER *php_openssl_get_evp_cipher_from_algo(zend_long algo) const EVP_CIPHER *php_openssl_get_evp_cipher_from_algo(zend_long algo)
@ -805,7 +820,7 @@ static int php_openssl_compare_func(Bucket *a, Bucket *b)
void php_openssl_get_cipher_methods(zval *return_value, bool aliases) void php_openssl_get_cipher_methods(zval *return_value, bool aliases)
{ {
array_init(return_value); array_init(return_value);
EVP_CIPHER_do_all_provided(OPENSSL_G(libctx), EVP_CIPHER_do_all_provided(PHP_OPENSSL_LIBCTX,
aliases ? php_openssl_add_cipher_or_alias : php_openssl_add_cipher, aliases ? php_openssl_add_cipher_or_alias : php_openssl_add_cipher,
return_value); return_value);
zend_hash_sort(Z_ARRVAL_P(return_value), php_openssl_compare_func, 1); zend_hash_sort(Z_ARRVAL_P(return_value), php_openssl_compare_func, 1);

View file

@ -70,15 +70,24 @@ struct php_openssl_errors {
int bottom; int bottom;
}; };
struct php_openssl_libctx {
#if PHP_OPENSSL_API_VERSION >= 0x30000
OSSL_LIB_CTX *libctx;
OSSL_LIB_CTX *default_libctx;
OSSL_LIB_CTX *custom_libctx;
#endif
char *propq;
};
ZEND_BEGIN_MODULE_GLOBALS(openssl) ZEND_BEGIN_MODULE_GLOBALS(openssl)
struct php_openssl_errors *errors; struct php_openssl_errors *errors;
struct php_openssl_errors *errors_mark; struct php_openssl_errors *errors_mark;
#if PHP_OPENSSL_API_VERSION >= 0x30000 struct php_openssl_libctx ctx;
OSSL_LIB_CTX *libctx;
char *propq;
#endif
ZEND_END_MODULE_GLOBALS(openssl) ZEND_END_MODULE_GLOBALS(openssl)
#define PHP_OPENSSL_LIBCTX OPENSSL_G(ctx).libctx
#define PHP_OPENSSL_PROPQ OPENSSL_G(ctx).propq
#define OPENSSL_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(openssl, v) #define OPENSSL_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(openssl, v)
#if defined(ZTS) && defined(COMPILE_DL_OPENSSL) #if defined(ZTS) && defined(COMPILE_DL_OPENSSL)

View file

@ -237,10 +237,8 @@ void php_openssl_backend_init_common(void);
void php_openssl_backend_gshutdown(void); void php_openssl_backend_gshutdown(void);
void php_openssl_backend_shutdown(void); void php_openssl_backend_shutdown(void);
#if PHP_OPENSSL_API_VERSION >= 0x30000 void php_openssl_backend_init_libctx(struct php_openssl_libctx *ctx);
void php_openssl_backend_init_libctx(OSSL_LIB_CTX **plibctx, char **ppropq); void php_openssl_backend_destroy_libctx(struct php_openssl_libctx *ctx);
void php_openssl_backend_destroy_libctx(OSSL_LIB_CTX *libctx, char *propq);
#endif
const char *php_openssl_get_conf_filename(void); const char *php_openssl_get_conf_filename(void);

View file

@ -1863,6 +1863,14 @@ ldap.max_links = -1
; SSL stream context option. ; SSL stream context option.
;openssl.capath= ;openssl.capath=
; The libctx is an OpenSSL library context. OpenSSL defines a default library
; context, but PHP OpenSSL also defines its own library context to avoid
; interference with other libraries using OpenSSL and to provide an independent
; context for each thread in ZTS. Possible values:
; "custom" - use a custom library context (default)
; "default" - use the default OpenSSL library context
;openssl.libctx=custom
[ffi] [ffi]
; FFI API restriction. Possible values: ; FFI API restriction. Possible values:
; "preload" - enabled in CLI scripts and preloaded files (default) ; "preload" - enabled in CLI scripts and preloaded files (default)

View file

@ -1865,6 +1865,14 @@ ldap.max_links = -1
; SSL stream context option. ; SSL stream context option.
;openssl.capath= ;openssl.capath=
; The libctx is an OpenSSL library context. OpenSSL defines a default library
; context, but PHP OpenSSL also defines its own library context to avoid
; interference with other libraries using OpenSSL and to provide an independent
; context for each thread in ZTS. Possible values:
; "custom" - use a custom library context (default)
; "default" - use the default OpenSSL library context
;openssl.libctx=custom
[ffi] [ffi]
; FFI API restriction. Possible values: ; FFI API restriction. Possible values:
; "preload" - enabled in CLI scripts and preloaded files (default) ; "preload" - enabled in CLI scripts and preloaded files (default)