Skip to content

Commit 1095cf5

Browse files
authored
ext/phar: split calling openssl_verify and openssl_sign functions (#21836)
The only thing vaguely similar is the setting up of arguments, and even that is not identical due to the by-ref arg for openssl_sign(). Splitting makes the code more legible, as we can add const qualifiers and use a zend_string directly, reducing allocations
1 parent 70f0ca5 commit 1095cf5

2 files changed

Lines changed: 98 additions & 63 deletions

File tree

ext/phar/phar_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_create_or_parse_filename(z
412412
ZEND_ATTRIBUTE_NONNULL_ARGS(3) zend_result phar_open_executed_filename(char *alias, size_t alias_len, char **error);
413413
zend_result phar_free_alias(const phar_archive_data *phar);
414414
zend_result phar_get_archive(phar_archive_data **archive, const char *fname, size_t fname_len, const char *alias, size_t alias_len, char **error);
415-
zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, char *sig, size_t sig_len, const char *fname, char **signature, size_t *signature_len, char **error);
415+
zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, const char *sig, size_t sig_len, const char *fname, char **signature, size_t *signature_len, char **error);
416416
ZEND_ATTRIBUTE_NONNULL zend_string* phar_create_signature(phar_archive_data *phar, php_stream *fp, char **error);
417417

418418
/* utility functions */

ext/phar/util.c

Lines changed: 97 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
#include <openssl/ssl.h>
3535
#include <openssl/pkcs12.h>
3636
#else
37-
static zend_result phar_call_openssl_signverify(bool is_sign, php_stream *fp, zend_off_t end, const char *key, size_t key_len, char **signature, size_t *signature_len, uint32_t sig_type);
37+
ZEND_ATTRIBUTE_NONNULL static bool phar_call_openssl_verify(php_stream *fp, zend_off_t end, zend_string *public_key, const char *signature, size_t signature_len, uint32_t sig_type);
38+
static zend_result phar_call_openssl_sign(php_stream *fp, zend_off_t end, const char *key, size_t key_len, char **signature, size_t *signature_len, uint32_t sig_type);
3839
#endif
3940

4041
/* for links to relative location, prepend cwd of the entry */
@@ -1372,36 +1373,44 @@ static int phar_hex_str(const char *digest, size_t digest_len, char **signature)
13721373
/* }}} */
13731374

13741375
#ifndef PHAR_HAVE_OPENSSL
1375-
static zend_result phar_call_openssl_signverify(bool is_sign, php_stream *fp, zend_off_t end, const char *key, size_t key_len, char **signature, size_t *signature_len, uint32_t sig_type) /* {{{ */
1376-
{
1377-
zval retval, zp[4];
1378-
zend_string *str;
1379-
1380-
zend_function *fn = NULL;
1381-
if (is_sign) {
1382-
fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("openssl_sign"));
1383-
} else {
1384-
fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("openssl_verify"));
1385-
}
1376+
ZEND_ATTRIBUTE_NONNULL static bool phar_call_openssl_verify(
1377+
php_stream *fp,
1378+
zend_off_t end,
1379+
zend_string *public_key,
1380+
const char *signature,
1381+
size_t signature_len,
1382+
uint32_t sig_type
1383+
) {
1384+
ZEND_ASSERT(signature_len != 0);
1385+
zend_function *fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("openssl_verify"));
13861386

13871387
/* OpenSSL is not available, even as a shared module */
1388-
if (fn == NULL) {
1389-
return FAILURE;
1388+
if (UNEXPECTED(fn == NULL)) {
1389+
return false;
13901390
}
13911391

1392-
if (*signature_len) {
1393-
ZVAL_STRINGL(&zp[1], *signature, *signature_len);
1394-
} else {
1395-
ZVAL_EMPTY_STRING(&zp[1]);
1396-
}
1397-
ZVAL_STRINGL(&zp[2], key, key_len);
1392+
/* Read and copy stream content */
13981393
php_stream_rewind(fp);
1399-
str = php_stream_copy_to_mem(fp, (size_t) end, 0);
1400-
if (str) {
1401-
ZVAL_STR(&zp[0], str);
1402-
} else {
1403-
ZVAL_EMPTY_STRING(&zp[0]);
1404-
}
1394+
zend_string *str = php_stream_copy_to_mem(fp, (size_t) end, false);
1395+
/* No content thus signing must fail */
1396+
if (UNEXPECTED(str == NULL)) {
1397+
return false;
1398+
}
1399+
1400+
/* Set up parameters for call to openssl_verify()
1401+
* openssl_verify(
1402+
* string $data,
1403+
* string $signature,
1404+
* OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $public_key,
1405+
* string|int $algorithm = OPENSSL_ALGO_SHA1,
1406+
* int $padding = 0
1407+
* ): int|false
1408+
*/
1409+
zval retval, zp[4];
1410+
ZVAL_STR(&zp[0], str);
1411+
ZVAL_STRINGL(&zp[1], signature, signature_len);
1412+
/* Note we do not own the lifetime of the public key, but it is fine as calling the function will increase the refcount) */
1413+
ZVAL_STR(&zp[2], public_key);
14051414
if (sig_type == PHAR_SIG_OPENSSL_SHA512) {
14061415
ZVAL_LONG(&zp[3], 9); /* value from openssl.c #define OPENSSL_ALGO_SHA512 9 */
14071416
} else if (sig_type == PHAR_SIG_OPENSSL_SHA256) {
@@ -1410,60 +1419,92 @@ static zend_result phar_call_openssl_signverify(bool is_sign, php_stream *fp, ze
14101419
/* don't rely on default value which may change in the future */
14111420
ZVAL_LONG(&zp[3], 1); /* value from openssl.c #define OPENSSL_ALGO_SHA1 1 */
14121421
}
1422+
zend_call_known_function(fn, NULL, NULL, &retval, /* param_count */ 4, zp, NULL);
1423+
/* Free string arguments that we own */
1424+
zval_ptr_dtor_str(&zp[0]);
1425+
zval_ptr_dtor_str(&zp[1]);
1426+
1427+
/* Returns 1 if the signature is correct, 0 if it is incorrect, and -1 or false on error. */
1428+
switch (Z_TYPE(retval)) {
1429+
case IS_LONG:
1430+
if (1 == Z_LVAL(retval)) {
1431+
return true;
1432+
}
1433+
ZEND_FALLTHROUGH;
1434+
default:
1435+
/* Unlikely, but the openssl_verify() function may be disabled and redefined in userland and return bollocks */
1436+
zval_ptr_dtor(&retval);
1437+
return false;
1438+
}
1439+
}
1440+
1441+
static zend_result phar_call_openssl_sign(php_stream *fp, zend_off_t end, const char *key, size_t key_len, char **signature, size_t *signature_len, uint32_t sig_type) /* {{{ */
1442+
{
1443+
ZEND_ASSERT(end != 0);
1444+
ZEND_ASSERT(*signature_len == 0);
1445+
zval retval, zp[4];
14131446

1414-
if ((size_t)end != Z_STRLEN(zp[0])) {
1415-
zval_ptr_dtor_str(&zp[0]);
1416-
zval_ptr_dtor_str(&zp[1]);
1417-
zval_ptr_dtor_str(&zp[2]);
1447+
zend_function *fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("openssl_sign"));
1448+
1449+
/* OpenSSL is not available, even as a shared module */
1450+
if (fn == NULL) {
1451+
return FAILURE;
1452+
}
1453+
1454+
/* Read and copy stream content */
1455+
php_stream_rewind(fp);
1456+
zend_string *str = php_stream_copy_to_mem(fp, (size_t) end, false);
1457+
/* No content thus signing must fail */
1458+
if (!str || (size_t)end != ZSTR_LEN(str)) {
14181459
return FAILURE;
14191460
}
14201461

1421-
Z_ADDREF(zp[0]);
1422-
if (is_sign) {
1423-
ZVAL_NEW_REF(&zp[1], &zp[1]);
1462+
/* Set up parameters for call to openssl_sign()
1463+
* openssl_sign(
1464+
* string $data,
1465+
* string &$signature,
1466+
* #[\SensitiveParameter] OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key,
1467+
* string|int $algorithm = OPENSSL_ALGO_SHA1,
1468+
* int $padding = 0
1469+
* ): bool
1470+
*/
1471+
ZVAL_STR(&zp[0], str);
1472+
ZVAL_EMPTY_STRING(&zp[1]);
1473+
ZVAL_NEW_REF(&zp[1], &zp[1]);
1474+
ZVAL_STRINGL(&zp[2], key, key_len);
1475+
if (sig_type == PHAR_SIG_OPENSSL_SHA512) {
1476+
ZVAL_LONG(&zp[3], 9); /* value from openssl.c #define OPENSSL_ALGO_SHA512 9 */
1477+
} else if (sig_type == PHAR_SIG_OPENSSL_SHA256) {
1478+
ZVAL_LONG(&zp[3], 7); /* value from openssl.c #define OPENSSL_ALGO_SHA256 7 */
14241479
} else {
1425-
Z_ADDREF(zp[1]);
1480+
/* don't rely on default value which may change in the future */
1481+
ZVAL_LONG(&zp[3], 1); /* value from openssl.c #define OPENSSL_ALGO_SHA1 1 */
14261482
}
1427-
Z_ADDREF(zp[2]);
14281483

14291484
zend_call_known_function(fn, NULL, NULL, &retval, /* param_count */ 4, zp, NULL);
14301485

1431-
Z_DELREF(zp[0]);
1432-
1433-
if (is_sign) {
1434-
ZVAL_UNREF(&zp[1]);
1435-
} else {
1436-
Z_DELREF(zp[1]);
1437-
}
1438-
Z_DELREF(zp[2]);
1486+
ZVAL_UNREF(&zp[1]);
14391487

14401488
zval_ptr_dtor_str(&zp[0]);
14411489
zval_ptr_dtor_str(&zp[2]);
14421490

14431491
switch (Z_TYPE(retval)) {
1444-
case IS_LONG:
1445-
zval_ptr_dtor(&zp[1]);
1446-
if (1 == Z_LVAL(retval)) {
1447-
return SUCCESS;
1448-
}
1449-
return FAILURE;
14501492
case IS_TRUE:
14511493
*signature = estrndup(Z_STRVAL(zp[1]), Z_STRLEN(zp[1]));
14521494
*signature_len = Z_STRLEN(zp[1]);
14531495
zval_ptr_dtor(&zp[1]);
14541496
return SUCCESS;
14551497
default:
1498+
/* Unlikely, but the openssl_sign() function may be disabled and redefined in userland and return bollocks */
14561499
zval_ptr_dtor(&retval);
1457-
ZEND_FALLTHROUGH;
1458-
case IS_FALSE:
14591500
zval_ptr_dtor(&zp[1]);
14601501
return FAILURE;
14611502
}
14621503
}
14631504
/* }}} */
14641505
#endif /* #ifndef PHAR_HAVE_OPENSSL */
14651506

1466-
zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, char *sig, size_t sig_len, const char *fname, char **signature, size_t *signature_len, char **error) /* {{{ */
1507+
zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, const char *sig, size_t sig_len, const char *fname, char **signature, size_t *signature_len, char **error) /* {{{ */
14671508
{
14681509
size_t read_size, len;
14691510
zend_off_t read_len;
@@ -1488,8 +1529,6 @@ zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t s
14881529
} else {
14891530
mdtype = EVP_sha1();
14901531
}
1491-
#else
1492-
size_t tempsig;
14931532
#endif
14941533
zend_string *pubkey = NULL;
14951534
char *pfile;
@@ -1524,9 +1563,7 @@ zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t s
15241563
}
15251564

15261565
#ifndef PHAR_HAVE_OPENSSL
1527-
tempsig = sig_len;
1528-
1529-
if (FAILURE == phar_call_openssl_signverify(false, fp, end_of_phar, ZSTR_VAL(pubkey), ZSTR_LEN(pubkey), &sig, &tempsig, sig_type)) {
1566+
if (!phar_call_openssl_verify(fp, end_of_phar, pubkey, sig, sig_len, sig_type)) {
15301567
zend_string_release_ex(pubkey, 0);
15311568

15321569
if (error) {
@@ -1536,9 +1573,7 @@ zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t s
15361573
return FAILURE;
15371574
}
15381575

1539-
zend_string_release_ex(pubkey, 0);
1540-
1541-
sig_len = tempsig;
1576+
zend_string_release_ex(pubkey, false);
15421577
#else
15431578
in = BIO_new_mem_buf(ZSTR_VAL(pubkey), ZSTR_LEN(pubkey));
15441579

@@ -1610,7 +1645,7 @@ zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t s
16101645
EVP_MD_CTX_destroy(md_ctx);
16111646
#endif
16121647

1613-
*signature_len = phar_hex_str((const char*)sig, sig_len, signature);
1648+
*signature_len = phar_hex_str(sig, sig_len, signature);
16141649
}
16151650
break;
16161651
case PHAR_SIG_SHA512: {
@@ -1909,7 +1944,7 @@ ZEND_ATTRIBUTE_NONNULL zend_string* phar_create_signature(phar_archive_data *pha
19091944
siglen = 0;
19101945
php_stream_seek(fp, 0, SEEK_END);
19111946

1912-
if (FAILURE == phar_call_openssl_signverify(true, fp, php_stream_tell(fp), PHAR_G(openssl_privatekey), PHAR_G(openssl_privatekey_len), (char **)&sigbuf, &siglen, phar->sig_flags)) {
1947+
if (FAILURE == phar_call_openssl_sign(fp, php_stream_tell(fp), PHAR_G(openssl_privatekey), PHAR_G(openssl_privatekey_len), (char **)&sigbuf, &siglen, phar->sig_flags)) {
19131948
spprintf(error, 0, "unable to write phar \"%s\" with requested openssl signature", ZSTR_VAL(phar->fname));
19141949
return NULL;
19151950
}

0 commit comments

Comments
 (0)