Skip to content

Commit ddbf829

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: ext/openssl: openssl_encrypt() zend mm heap overflow on AES-WRAP-PAD mode.
2 parents a9987fa + 1692268 commit ddbf829

2 files changed

Lines changed: 46 additions & 1 deletion

File tree

ext/openssl/openssl_backend_common.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,7 @@ zend_result php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
18071807
const char *aad, size_t aad_len, int enc)
18081808
{
18091809
int i = 0;
1810+
size_t outlen = data_len + EVP_CIPHER_block_size(cipher_type);
18101811

18111812
if (mode->is_single_run_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, NULL, (int)data_len)) {
18121813
php_openssl_store_errors();
@@ -1820,7 +1821,19 @@ zend_result php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
18201821
return FAILURE;
18211822
}
18221823

1823-
*poutbuf = zend_string_alloc((int)data_len + EVP_CIPHER_block_size(cipher_type), 0);
1824+
#ifdef EVP_CIPH_WRAP_MODE
1825+
if ((EVP_CIPHER_mode(cipher_type)) == EVP_CIPH_WRAP_MODE) {
1826+
/*
1827+
* RFC 5649 wrap-with-padding rounds the input up to the block size
1828+
* and prepends an integrity block, we reserve one extra block.
1829+
* See EVP_EncryptUpdate(3): wrap mode may write up to
1830+
* inl + cipher_block_size bytes.
1831+
*/
1832+
outlen += EVP_CIPHER_block_size(cipher_type);
1833+
}
1834+
#endif
1835+
1836+
*poutbuf = zend_string_alloc(outlen, false);
18241837

18251838
if (!EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf),
18261839
&i, (const unsigned char *)data, (int)data_len)) {

ext/openssl/tests/gh22186.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
GH-22186 (Heap buffer overflow in openssl_encrypt with AES-WRAP-PAD)
3+
--EXTENSIONS--
4+
openssl
5+
--SKIPIF--
6+
<?php
7+
/* openssl_get_cipher_methods() enumerates provider ciphers, but openssl_encrypt()
8+
* resolves names via the legacy EVP_get_cipherbyname(), so on some builds the
9+
* cipher is listed yet not usable. Probe the actual call path instead. */
10+
if (!@openssl_encrypt("test", "aes-128-wrap-pad", str_repeat("k", 16),
11+
OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, str_repeat("\0", 4))) {
12+
die('skip aes-128-wrap-pad not usable on this OpenSSL build');
13+
}
14+
?>
15+
--FILE--
16+
<?php
17+
$pass = str_repeat("k", 16);
18+
$iv = str_repeat("\0", 4);
19+
20+
for ($i = 1; $i < 258; $i++) {
21+
$data = str_repeat("a", $i);
22+
$enc = openssl_encrypt($data, 'aes-128-wrap-pad', $pass, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, $iv);
23+
$dec = openssl_decrypt($enc, 'aes-128-wrap-pad', $pass, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, $iv);
24+
if ($dec !== $data) {
25+
die("mismatch at $i\n");
26+
}
27+
}
28+
29+
echo "done\n";
30+
?>
31+
--EXPECT--
32+
done

0 commit comments

Comments
 (0)