diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 6518a719314fd..c34a8dae82490 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -351,12 +351,39 @@ int php_openssl_get_ssl_stream_data_index(void) return ssl_stream_data_index; } -/* {{{ INI Settings */ +static PHP_INI_MH(OnUpdateLibCtx) +{ +#if PHP_OPENSSL_API_VERSION >= 0x30000 + const char *name = ZSTR_VAL(new_value); + + if (!strcmp(name, "default")) { + OPENSSL_G(ctx).libctx = OPENSSL_G(ctx).default_libctx; + } else if (!strcmp(name, "custom")) { + OPENSSL_G(ctx).libctx = OPENSSL_G(ctx).custom_libctx; + } else { + int err_type; + if (stage == ZEND_INI_STAGE_RUNTIME) { + err_type = E_WARNING; + } else { + err_type = E_ERROR; + } + + /* Do not output error when restoring ini options. */ + if (stage != ZEND_INI_STAGE_DEACTIVATE) { + php_error_docref(NULL, err_type, "OpenSSL libctx \"%s\" cannot be found", name); + } + return FAILURE; + } +#endif + + return SUCCESS; +} + PHP_INI_BEGIN() PHP_INI_ENTRY("openssl.cafile", 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_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(openssl) @@ -438,9 +465,7 @@ PHP_GINIT_FUNCTION(openssl) #endif openssl_globals->errors = NULL; openssl_globals->errors_mark = NULL; -#if PHP_OPENSSL_API_VERSION >= 0x30000 - php_openssl_backend_init_libctx(&openssl_globals->libctx, &openssl_globals->propq); -#endif + php_openssl_backend_init_libctx(&openssl_globals->ctx); } /* }}} */ @@ -453,9 +478,7 @@ PHP_GSHUTDOWN_FUNCTION(openssl) if (openssl_globals->errors_mark) { pefree(openssl_globals->errors_mark, 1); } -#if PHP_OPENSSL_API_VERSION >= 0x30000 - php_openssl_backend_destroy_libctx(openssl_globals->libctx, openssl_globals->propq); -#endif + php_openssl_backend_destroy_libctx(&openssl_globals->ctx); } /* }}} */ diff --git a/ext/openssl/openssl_backend_v1.c b/ext/openssl/openssl_backend_v1.c index 8b9ede38437d6..44b4e8a67a174 100644 --- a/ext/openssl/openssl_backend_v1.c +++ b/ext/openssl/openssl_backend_v1.c @@ -44,6 +44,16 @@ void php_openssl_backend_shutdown(void) #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) { return EVP_PKEY_CTX_new_id(id, NULL); diff --git a/ext/openssl/openssl_backend_v3.c b/ext/openssl/openssl_backend_v3.c index e3c80cf659ba1..b542fc6c93e25 100644 --- a/ext/openssl/openssl_backend_v3.c +++ b/ext/openssl/openssl_backend_v3.c @@ -29,32 +29,47 @@ void php_openssl_backend_shutdown(void) (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 - * (default context) is probably better. */ - *plibctx = OSSL_LIB_CTX_new(); - *ppropq = NULL; + ctx->default_libctx = OSSL_LIB_CTX_get0_global_default(); + ctx->custom_libctx = OSSL_LIB_CTX_new(); + if (ctx->custom_libctx != 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) { - OSSL_LIB_CTX_free(libctx); + if (ctx->custom_libctx != NULL) { + OSSL_LIB_CTX_free(ctx->custom_libctx); } - if (propq != NULL) { - free(propq); + if (ctx->propq != NULL) { + free(ctx->propq); } } 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) { - 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) @@ -299,7 +314,7 @@ EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) { 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; } @@ -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) { - 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[] = { @@ -754,7 +769,7 @@ static const char *php_openssl_cipher_names[] = { 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) @@ -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) { 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, return_value); zend_hash_sort(Z_ARRVAL_P(return_value), php_openssl_compare_func, 1); diff --git a/ext/openssl/php_openssl.h b/ext/openssl/php_openssl.h index 67522f3d34717..92ccd9a546f9e 100644 --- a/ext/openssl/php_openssl.h +++ b/ext/openssl/php_openssl.h @@ -70,15 +70,24 @@ struct php_openssl_errors { int bottom; }; -ZEND_BEGIN_MODULE_GLOBALS(openssl) - struct php_openssl_errors *errors; - struct php_openssl_errors *errors_mark; +struct php_openssl_libctx { #if PHP_OPENSSL_API_VERSION >= 0x30000 OSSL_LIB_CTX *libctx; - char *propq; + OSSL_LIB_CTX *default_libctx; + OSSL_LIB_CTX *custom_libctx; #endif + char *propq; +}; + +ZEND_BEGIN_MODULE_GLOBALS(openssl) + struct php_openssl_errors *errors; + struct php_openssl_errors *errors_mark; + struct php_openssl_libctx ctx; 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) #if defined(ZTS) && defined(COMPILE_DL_OPENSSL) diff --git a/ext/openssl/php_openssl_backend.h b/ext/openssl/php_openssl_backend.h index 69317e3c7833b..4717ac26c4e74 100644 --- a/ext/openssl/php_openssl_backend.h +++ b/ext/openssl/php_openssl_backend.h @@ -237,10 +237,8 @@ void php_openssl_backend_init_common(void); void php_openssl_backend_gshutdown(void); void php_openssl_backend_shutdown(void); -#if PHP_OPENSSL_API_VERSION >= 0x30000 -void php_openssl_backend_init_libctx(OSSL_LIB_CTX **plibctx, char **ppropq); -void php_openssl_backend_destroy_libctx(OSSL_LIB_CTX *libctx, char *propq); -#endif +void php_openssl_backend_init_libctx(struct php_openssl_libctx *ctx); +void php_openssl_backend_destroy_libctx(struct php_openssl_libctx *ctx); const char *php_openssl_get_conf_filename(void); diff --git a/php.ini-development b/php.ini-development index 162fb3f25c19c..4e18abb6e36f9 100644 --- a/php.ini-development +++ b/php.ini-development @@ -1857,6 +1857,14 @@ ldap.max_links = -1 ; SSL stream context option. ;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 API restriction. Possible values: ; "preload" - enabled in CLI scripts and preloaded files (default) diff --git a/php.ini-production b/php.ini-production index 042d246943d81..da8b8a64bc844 100644 --- a/php.ini-production +++ b/php.ini-production @@ -1859,6 +1859,14 @@ ldap.max_links = -1 ; SSL stream context option. ;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 API restriction. Possible values: ; "preload" - enabled in CLI scripts and preloaded files (default)