diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index aab240dc8506a..6518a719314fd 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -583,7 +583,7 @@ PHP_FUNCTION(openssl_spki_new) zval *zpkey = NULL; EVP_PKEY *pkey = NULL; NETSCAPE_SPKI *spki=NULL; - const EVP_MD *mdtype; + const EVP_MD *mdtype = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|l", &zpkey, php_openssl_pkey_ce, &challenge, &challenge_len, &algo) == FAILURE) { RETURN_THROWS(); @@ -647,6 +647,7 @@ PHP_FUNCTION(openssl_spki_new) goto cleanup; cleanup: + php_openssl_release_evp_md(mdtype); EVP_PKEY_free(pkey); if (spki != NULL) { NETSCAPE_SPKI_free(spki); @@ -1821,7 +1822,6 @@ PHP_FUNCTION(openssl_csr_sign) X509_free(new_cert); } - PHP_SSL_REQ_DISPOSE(&req); EVP_PKEY_free(priv_key); EVP_PKEY_free(key); if (csr_str) { @@ -1830,6 +1830,7 @@ PHP_FUNCTION(openssl_csr_sign) if (cert_str && cert && cert != new_cert) { X509_free(cert); } + PHP_SSL_REQ_DISPOSE(&req); } /* }}} */ @@ -2104,7 +2105,8 @@ PHP_FUNCTION(openssl_pkey_export_to_file) } if (!php_openssl_check_path(filename, filename_len, file_path, 2)) { - goto clean_exit_key; + EVP_PKEY_free(key); + return; } PHP_SSL_REQ_INIT(&req); @@ -2139,10 +2141,9 @@ PHP_FUNCTION(openssl_pkey_export_to_file) } clean_exit: - PHP_SSL_REQ_DISPOSE(&req); BIO_free(bio_out); -clean_exit_key: EVP_PKEY_free(key); + PHP_SSL_REQ_DISPOSE(&req); } /* }}} */ @@ -2204,9 +2205,9 @@ PHP_FUNCTION(openssl_pkey_export) php_openssl_store_errors(); } } - PHP_SSL_REQ_DISPOSE(&req); EVP_PKEY_free(key); BIO_free(bio_out); + PHP_SSL_REQ_DISPOSE(&req); } /* }}} */ @@ -2686,6 +2687,7 @@ PHP_FUNCTION(openssl_pkcs7_encrypt) if (recipcerts) { sk_X509_pop_free(recipcerts, X509_free); } + php_openssl_release_evp_cipher(cipher); } /* }}} */ @@ -3334,6 +3336,7 @@ PHP_FUNCTION(openssl_cms_encrypt) if (recipcerts) { sk_X509_pop_free(recipcerts, X509_free); } + php_openssl_release_evp_cipher(cipher); } /* }}} */ @@ -3969,7 +3972,7 @@ PHP_FUNCTION(openssl_sign) } if (method_str) { - mdtype = EVP_get_digestbyname(ZSTR_VAL(method_str)); + mdtype = php_openssl_get_evp_md_by_name(ZSTR_VAL(method_str)); } else { mdtype = php_openssl_get_evp_md_from_algo(method_long); } @@ -3996,6 +3999,7 @@ PHP_FUNCTION(openssl_sign) RETVAL_FALSE; } EVP_MD_CTX_destroy(md_ctx); + php_openssl_release_evp_md(mdtype); EVP_PKEY_free(pkey); } /* }}} */ @@ -4027,7 +4031,7 @@ PHP_FUNCTION(openssl_verify) PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(signature_len, signature, 2); if (method_str) { - mdtype = EVP_get_digestbyname(ZSTR_VAL(method_str)); + mdtype = php_openssl_get_evp_md_by_name(ZSTR_VAL(method_str)); } else { mdtype = php_openssl_get_evp_md_from_algo(method_long); } @@ -4041,6 +4045,7 @@ PHP_FUNCTION(openssl_verify) if (!EG(exception)) { php_error_docref(NULL, E_WARNING, "Supplied key param cannot be coerced into a public key"); } + php_openssl_release_evp_md(mdtype); RETURN_FALSE; } @@ -4051,6 +4056,7 @@ PHP_FUNCTION(openssl_verify) php_openssl_store_errors(); } EVP_MD_CTX_destroy(md_ctx); + php_openssl_release_evp_md(mdtype); EVP_PKEY_free(pkey); RETURN_LONG(err); } @@ -4323,7 +4329,7 @@ PHP_FUNCTION(openssl_digest) if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) { RETURN_THROWS(); } - mdtype = EVP_get_digestbyname(method); + mdtype = php_openssl_get_evp_md_by_name(method); if (!mdtype) { php_error_docref(NULL, E_WARNING, "Unknown digest algorithm"); RETURN_FALSE; @@ -4356,6 +4362,7 @@ PHP_FUNCTION(openssl_digest) } EVP_MD_CTX_destroy(md_ctx); + php_openssl_release_evp_md(mdtype); } /* }}} */ diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c index 0cda27407a567..0cddf2fa09f45 100644 --- a/ext/openssl/openssl_backend_common.c +++ b/ext/openssl/openssl_backend_common.c @@ -369,11 +369,11 @@ int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args if (strcmp(req->digest_name, "null") == 0) { req->digest = req->md_alg = EVP_md_null(); } else { - req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name); + req->digest = req->md_alg = php_openssl_get_evp_md_by_name(req->digest_name); } } if (req->md_alg == NULL) { - req->md_alg = req->digest = EVP_sha1(); + req->md_alg = req->digest = php_openssl_get_evp_md_by_name("sha1"); php_openssl_store_errors(); } @@ -417,6 +417,10 @@ void php_openssl_dispose_config(struct php_x509_request * req) NCONF_free(req->req_config); req->req_config = NULL; } + if (req->md_alg != NULL && req->md_alg != EVP_md_null()) { + php_openssl_release_evp_md(req->md_alg); + } + php_openssl_release_evp_cipher(req->priv_key_encrypt_cipher); } zend_result php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded) @@ -469,92 +473,6 @@ zend_result php_openssl_write_rand_file(const char * file, int egdsocket, int se return SUCCESS; } -EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo) { - EVP_MD *mdtype; - - switch (algo) { - case OPENSSL_ALGO_SHA1: - mdtype = (EVP_MD *) EVP_sha1(); - break; - case OPENSSL_ALGO_MD5: - mdtype = (EVP_MD *) EVP_md5(); - break; -#ifndef OPENSSL_NO_MD4 - case OPENSSL_ALGO_MD4: - mdtype = (EVP_MD *) EVP_md4(); - break; -#endif -#ifndef OPENSSL_NO_MD2 - case OPENSSL_ALGO_MD2: - mdtype = (EVP_MD *) EVP_md2(); - break; -#endif - case OPENSSL_ALGO_SHA224: - mdtype = (EVP_MD *) EVP_sha224(); - break; - case OPENSSL_ALGO_SHA256: - mdtype = (EVP_MD *) EVP_sha256(); - break; - case OPENSSL_ALGO_SHA384: - mdtype = (EVP_MD *) EVP_sha384(); - break; - case OPENSSL_ALGO_SHA512: - mdtype = (EVP_MD *) EVP_sha512(); - break; -#ifndef OPENSSL_NO_RMD160 - case OPENSSL_ALGO_RMD160: - mdtype = (EVP_MD *) EVP_ripemd160(); - break; -#endif - default: - return NULL; - break; - } - return mdtype; -} - -const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo) { - switch (algo) { -#ifndef OPENSSL_NO_RC2 - case PHP_OPENSSL_CIPHER_RC2_40: - return EVP_rc2_40_cbc(); - break; - case PHP_OPENSSL_CIPHER_RC2_64: - return EVP_rc2_64_cbc(); - break; - case PHP_OPENSSL_CIPHER_RC2_128: - return EVP_rc2_cbc(); - break; -#endif - -#ifndef OPENSSL_NO_DES - case PHP_OPENSSL_CIPHER_DES: - return EVP_des_cbc(); - break; - case PHP_OPENSSL_CIPHER_3DES: - return EVP_des_ede3_cbc(); - break; -#endif - -#ifndef OPENSSL_NO_AES - case PHP_OPENSSL_CIPHER_AES_128_CBC: - return EVP_aes_128_cbc(); - break; - case PHP_OPENSSL_CIPHER_AES_192_CBC: - return EVP_aes_192_cbc(); - break; - case PHP_OPENSSL_CIPHER_AES_256_CBC: - return EVP_aes_256_cbc(); - break; -#endif - - - default: - return NULL; - break; - } -} - void php_openssl_backend_init(void) { #ifdef LIBRESSL_VERSION_NUMBER @@ -1931,7 +1849,7 @@ PHP_OPENSSL_API zend_string* php_openssl_encrypt( PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(tag_len, tag_len); - cipher_type = EVP_get_cipherbyname(method); + cipher_type = php_openssl_get_evp_cipher_by_name(method); if (!cipher_type) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); return NULL; @@ -1939,6 +1857,7 @@ PHP_OPENSSL_API zend_string* php_openssl_encrypt( cipher_ctx = EVP_CIPHER_CTX_new(); if (!cipher_ctx) { + php_openssl_release_evp_cipher(cipher_type); php_error_docref(NULL, E_WARNING, "Failed to create cipher context"); return NULL; } @@ -1997,6 +1916,7 @@ PHP_OPENSSL_API zend_string* php_openssl_encrypt( } EVP_CIPHER_CTX_reset(cipher_ctx); EVP_CIPHER_CTX_free(cipher_ctx); + php_openssl_release_evp_cipher(cipher_type); return outbuf; } @@ -2023,7 +1943,7 @@ PHP_OPENSSL_API zend_string* php_openssl_decrypt( PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(tag_len, tag); - cipher_type = EVP_get_cipherbyname(method); + cipher_type = php_openssl_get_evp_cipher_by_name(method); if (!cipher_type) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); return NULL; @@ -2031,6 +1951,7 @@ PHP_OPENSSL_API zend_string* php_openssl_decrypt( cipher_ctx = EVP_CIPHER_CTX_new(); if (!cipher_ctx) { + php_openssl_release_evp_cipher(cipher_type); php_error_docref(NULL, E_WARNING, "Failed to create cipher context"); return NULL; } @@ -2076,14 +1997,15 @@ PHP_OPENSSL_API zend_string* php_openssl_decrypt( } EVP_CIPHER_CTX_reset(cipher_ctx); EVP_CIPHER_CTX_free(cipher_ctx); + php_openssl_release_evp_cipher(cipher_type); return outbuf; } -const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *method) +const EVP_CIPHER *php_openssl_get_evp_cipher_by_name_with_warning(const char *method) { const EVP_CIPHER *cipher_type; - cipher_type = EVP_get_cipherbyname(method); + cipher_type = php_openssl_get_evp_cipher_by_name(method); if (!cipher_type) { php_error_docref(NULL, E_WARNING, "Unknown cipher algorithm"); return NULL; @@ -2095,16 +2017,26 @@ const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *method) PHP_OPENSSL_API zend_long php_openssl_cipher_iv_length(const char *method) { - const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name(method); + const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name_with_warning(method); + if (cipher_type == NULL) { + return -1; + } + int iv_length = EVP_CIPHER_iv_length(cipher_type); + php_openssl_release_evp_cipher(cipher_type); - return cipher_type == NULL ? -1 : EVP_CIPHER_iv_length(cipher_type); + return iv_length; } PHP_OPENSSL_API zend_long php_openssl_cipher_key_length(const char *method) { - const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name(method); + const EVP_CIPHER *cipher_type = php_openssl_get_evp_cipher_by_name_with_warning(method); + if (cipher_type == NULL) { + return -1; + } + int key_length = EVP_CIPHER_key_length(cipher_type); + php_openssl_release_evp_cipher(cipher_type); - return cipher_type == NULL ? -1 : EVP_CIPHER_key_length(cipher_type); + return key_length; } PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long buffer_length) diff --git a/ext/openssl/openssl_backend_v1.c b/ext/openssl/openssl_backend_v1.c index eb94ee3fbe4bc..8b9ede38437d6 100644 --- a/ext/openssl/openssl_backend_v1.c +++ b/ext/openssl/openssl_backend_v1.c @@ -561,6 +561,114 @@ zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pu return data; } +const EVP_MD *php_openssl_get_evp_md_by_name(const char *name) +{ + return EVP_get_digestbyname(name); +} + +const EVP_MD *php_openssl_get_evp_md_from_algo(zend_long algo) +{ + EVP_MD *mdtype; + + switch (algo) { + case OPENSSL_ALGO_SHA1: + mdtype = (EVP_MD *) EVP_sha1(); + break; + case OPENSSL_ALGO_MD5: + mdtype = (EVP_MD *) EVP_md5(); + break; +#ifndef OPENSSL_NO_MD4 + case OPENSSL_ALGO_MD4: + mdtype = (EVP_MD *) EVP_md4(); + break; +#endif +#ifndef OPENSSL_NO_MD2 + case OPENSSL_ALGO_MD2: + mdtype = (EVP_MD *) EVP_md2(); + break; +#endif + case OPENSSL_ALGO_SHA224: + mdtype = (EVP_MD *) EVP_sha224(); + break; + case OPENSSL_ALGO_SHA256: + mdtype = (EVP_MD *) EVP_sha256(); + break; + case OPENSSL_ALGO_SHA384: + mdtype = (EVP_MD *) EVP_sha384(); + break; + case OPENSSL_ALGO_SHA512: + mdtype = (EVP_MD *) EVP_sha512(); + break; +#ifndef OPENSSL_NO_RMD160 + case OPENSSL_ALGO_RMD160: + mdtype = (EVP_MD *) EVP_ripemd160(); + break; +#endif + default: + return NULL; + break; + } + return mdtype; +} + +void php_openssl_release_evp_md(const EVP_MD *md) +{ + // Do nothing as MD is static +} + +const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *name) +{ + return EVP_get_cipherbyname(name); +} + +const EVP_CIPHER *php_openssl_get_evp_cipher_from_algo(zend_long algo) +{ + switch (algo) { +#ifndef OPENSSL_NO_RC2 + case PHP_OPENSSL_CIPHER_RC2_40: + return EVP_rc2_40_cbc(); + break; + case PHP_OPENSSL_CIPHER_RC2_64: + return EVP_rc2_64_cbc(); + break; + case PHP_OPENSSL_CIPHER_RC2_128: + return EVP_rc2_cbc(); + break; +#endif + +#ifndef OPENSSL_NO_DES + case PHP_OPENSSL_CIPHER_DES: + return EVP_des_cbc(); + break; + case PHP_OPENSSL_CIPHER_3DES: + return EVP_des_ede3_cbc(); + break; +#endif + +#ifndef OPENSSL_NO_AES + case PHP_OPENSSL_CIPHER_AES_128_CBC: + return EVP_aes_128_cbc(); + break; + case PHP_OPENSSL_CIPHER_AES_192_CBC: + return EVP_aes_192_cbc(); + break; + case PHP_OPENSSL_CIPHER_AES_256_CBC: + return EVP_aes_256_cbc(); + break; +#endif + + + default: + return NULL; + break; + } +} + +void php_openssl_release_evp_cipher(const EVP_CIPHER *cipher) +{ + // Do nothing as the cipher is static +} + void php_openssl_get_cipher_methods(zval *return_value, bool aliases) { array_init(return_value); diff --git a/ext/openssl/openssl_backend_v3.c b/ext/openssl/openssl_backend_v3.c index 0876690aee0ba..e3c80cf659ba1 100644 --- a/ext/openssl/openssl_backend_v3.c +++ b/ext/openssl/openssl_backend_v3.c @@ -696,6 +696,89 @@ zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pu return result; } +const EVP_MD *php_openssl_get_evp_md_by_name(const char *name) +{ + return EVP_MD_fetch(OPENSSL_G(libctx), name, OPENSSL_G(propq)); +} + +static const char *php_openssl_digest_names[] = { + [OPENSSL_ALGO_SHA1] = "SHA1", + [OPENSSL_ALGO_MD5] = "MD5", +#ifndef OPENSSL_NO_MD4 + [OPENSSL_ALGO_MD4] = "MD4", +#endif +#ifndef OPENSSL_NO_MD2 + [OPENSSL_ALGO_MD2] = "MD2", +#endif + [OPENSSL_ALGO_SHA224] = "SHA224", + [OPENSSL_ALGO_SHA256] = "SHA256", + [OPENSSL_ALGO_SHA384] = "SHA384", + [OPENSSL_ALGO_SHA512] = "SHA512", +#ifndef OPENSSL_NO_RMD160 + [OPENSSL_ALGO_RMD160] = "RIPEMD160", +#endif +}; + +const EVP_MD *php_openssl_get_evp_md_from_algo(zend_long algo) +{ + if (algo < 0 || algo >= (zend_long)(sizeof(php_openssl_digest_names) / sizeof(*php_openssl_digest_names))) { + return NULL; + } + + const char *name = php_openssl_digest_names[algo]; + if (!name) { + return NULL; + } + + return php_openssl_get_evp_md_by_name(name); +} + +void php_openssl_release_evp_md(const EVP_MD *md) +{ + if (md != NULL) { + // It is fine to remove const as the md is from EVP_MD_fetch + EVP_MD_free((EVP_MD *) md); + } +} + +static const char *php_openssl_cipher_names[] = { + [PHP_OPENSSL_CIPHER_RC2_40] = "RC2-40-CBC", + [PHP_OPENSSL_CIPHER_RC2_128] = "RC2-CBC", + [PHP_OPENSSL_CIPHER_RC2_64] = "RC2-64-CBC", + [PHP_OPENSSL_CIPHER_DES] = "DES-CBC", + [PHP_OPENSSL_CIPHER_3DES] = "DES-EDE3-CBC", + [PHP_OPENSSL_CIPHER_AES_128_CBC]= "AES-128-CBC", + [PHP_OPENSSL_CIPHER_AES_192_CBC]= "AES-192-CBC", + [PHP_OPENSSL_CIPHER_AES_256_CBC]= "AES-256-CBC", +}; + +const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *name) +{ + return EVP_CIPHER_fetch(OPENSSL_G(libctx), name, OPENSSL_G(propq)); +} + +const EVP_CIPHER *php_openssl_get_evp_cipher_from_algo(zend_long algo) +{ + if (algo < 0 || algo >= (zend_long)(sizeof(php_openssl_cipher_names) / sizeof(*php_openssl_cipher_names))) { + return NULL; + } + + const char *name = php_openssl_cipher_names[algo]; + if (!name) { + return NULL; + } + + return php_openssl_get_evp_cipher_by_name(name); +} + +void php_openssl_release_evp_cipher(const EVP_CIPHER *cipher) +{ + if (cipher != NULL) { + // It is fine to remove const as the cipher is from EVP_CIPHER_fetch + EVP_CIPHER_free((EVP_CIPHER *) cipher); + } +} + static void php_openssl_add_cipher_name(const char *name, void *arg) { size_t len = strlen(name); @@ -722,7 +805,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(NULL, + EVP_CIPHER_do_all_provided(OPENSSL_G(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_backend.h b/ext/openssl/php_openssl_backend.h index 5cfa73160b853..69317e3c7833b 100644 --- a/ext/openssl/php_openssl_backend.h +++ b/ext/openssl/php_openssl_backend.h @@ -75,14 +75,17 @@ enum php_openssl_key_type { OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_DSA, OPENSSL_KEYTYPE_DH, + OPENSSL_KEYTYPE_EC, + OPENSSL_KEYTYPE_X25519, + OPENSSL_KEYTYPE_ED25519, + OPENSSL_KEYTYPE_X448, + OPENSSL_KEYTYPE_ED448, + OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA, - OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1, - OPENSSL_KEYTYPE_X25519 = OPENSSL_KEYTYPE_DH +2, - OPENSSL_KEYTYPE_ED25519 = OPENSSL_KEYTYPE_DH +3, - OPENSSL_KEYTYPE_X448 = OPENSSL_KEYTYPE_DH +4, - OPENSSL_KEYTYPE_ED448 = OPENSSL_KEYTYPE_DH +5, }; +/* Cipher constants, do not forget to update php_openssl_cipher_names in + * openssl_backend_v3.c if new constant added. */ enum php_openssl_cipher_type { PHP_OPENSSL_CIPHER_RC2_40, PHP_OPENSSL_CIPHER_RC2_128, @@ -106,10 +109,10 @@ enum php_openssl_encoding { ENCODING_PEM, }; - #define MIN_KEY_LENGTH 384 -/* constants used in ext/phar/util.c, keep in sync */ +/* Constants used in ext/phar/util.c, keep in sync and do not forget to update + * php_openssl_digest_names in openssl_backend_v3.c if new constant added. */ #define OPENSSL_ALGO_SHA1 1 #define OPENSSL_ALGO_MD5 2 #ifndef OPENSSL_NO_MD4 @@ -126,6 +129,7 @@ enum php_openssl_encoding { #ifndef OPENSSL_NO_RMD160 #define OPENSSL_ALGO_RMD160 10 #endif + #define DEBUG_SMIME 0 #if !defined(OPENSSL_NO_EC) && defined(EVP_PKEY_EC) @@ -221,8 +225,12 @@ void php_openssl_dispose_config(struct php_x509_request * req); zend_result php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded); zend_result php_openssl_write_rand_file(const char * file, int egdsocket, int seeded); -EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo); +const EVP_MD *php_openssl_get_evp_md_by_name(const char *name); +const EVP_MD *php_openssl_get_evp_md_from_algo(zend_long algo); +void php_openssl_release_evp_md(const EVP_MD *md); +const EVP_CIPHER * php_openssl_get_evp_cipher_by_name(const char *name); const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo); +void php_openssl_release_evp_cipher(const EVP_CIPHER *cipher); void php_openssl_backend_init(void); void php_openssl_backend_init_common(void);