diff options
| author | jvoisin | 2022-03-20 18:20:45 +0100 |
|---|---|---|
| committer | jvoisin | 2022-03-20 18:20:45 +0100 |
| commit | 81dd7f2ef07af306fe83d7755cbac4529aa9fc8d (patch) | |
| tree | 32cc44c6231b30db5ac7b15699297863460784aa /src/sp_unserialize.c | |
| parent | 83b01942dfc80474cc05e09aeef4b44307a7120b (diff) | |
| parent | c38df1077a6c1dfbca1baca049214d053e2e7684 (diff) | |
Merge remote-tracking branch 'sektioneins/master'
Diffstat (limited to 'src/sp_unserialize.c')
| -rw-r--r-- | src/sp_unserialize.c | 117 |
1 files changed, 70 insertions, 47 deletions
diff --git a/src/sp_unserialize.c b/src/sp_unserialize.c index 8977dd9..c2173d3 100644 --- a/src/sp_unserialize.c +++ b/src/sp_unserialize.c | |||
| @@ -1,28 +1,70 @@ | |||
| 1 | #include "php_snuffleupagus.h" | 1 | #include "php_snuffleupagus.h" |
| 2 | 2 | ||
| 3 | // condensed version of PHP's php_hash_do_hash_hmac() in ext/hash/hash.c | ||
| 4 | #if PHP_VERSION_ID < 80000 | ||
| 5 | static inline void *php_hash_alloc_context(const php_hash_ops *ops) { | ||
| 6 | /* Zero out context memory so serialization doesn't expose internals */ | ||
| 7 | return ecalloc(1, ops->context_size); | ||
| 8 | } | ||
| 9 | #endif | ||
| 10 | |||
| 11 | static zend_string *sp_do_hash_hmac_sha256(char *data, size_t data_len, char *key, size_t key_len) | ||
| 12 | { | ||
| 13 | #if PHP_VERSION_ID < 80000 | ||
| 14 | const php_hash_ops *ops = php_hash_fetch_ops(ZEND_STRL("sha256")); | ||
| 15 | #else | ||
| 16 | zend_string *algo = zend_string_init(ZEND_STRL("sha256"), 0); | ||
| 17 | const php_hash_ops *ops = php_hash_fetch_ops(algo); | ||
| 18 | zend_string_release_ex(algo, 0); | ||
| 19 | #endif | ||
| 20 | |||
| 21 | if (!ops || !ops->is_crypto) { | ||
| 22 | sp_log_err("hmac", "unsupported hash algorithm: sha256"); | ||
| 23 | return NULL; | ||
| 24 | } | ||
| 25 | |||
| 26 | void *context = php_hash_alloc_context(ops); | ||
| 27 | |||
| 28 | unsigned char *K = emalloc(ops->block_size); | ||
| 29 | zend_string *digest = zend_string_alloc(ops->digest_size, 0); | ||
| 30 | |||
| 31 | php_hash_hmac_prep_key(K, ops, context, (unsigned char *) key, key_len); | ||
| 32 | php_hash_hmac_round((unsigned char *) ZSTR_VAL(digest), ops, context, K, (unsigned char *) data, data_len); | ||
| 33 | php_hash_string_xor_char(K, K, 0x6A, ops->block_size); | ||
| 34 | php_hash_hmac_round((unsigned char *) ZSTR_VAL(digest), ops, context, K, (unsigned char *) ZSTR_VAL(digest), ops->digest_size); | ||
| 35 | |||
| 36 | /* Zero the key */ | ||
| 37 | ZEND_SECURE_ZERO(K, ops->block_size); | ||
| 38 | efree(K); | ||
| 39 | efree(context); | ||
| 40 | |||
| 41 | zend_string *hex_digest = zend_string_safe_alloc(ops->digest_size, 2, 0, 0); | ||
| 42 | |||
| 43 | php_hash_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), ops->digest_size); | ||
| 44 | ZSTR_VAL(hex_digest)[2 * ops->digest_size] = 0; | ||
| 45 | zend_string_release_ex(digest, 0); | ||
| 46 | return hex_digest; | ||
| 47 | } | ||
| 48 | |||
| 49 | // ------------------ | ||
| 50 | |||
| 3 | PHP_FUNCTION(sp_serialize) { | 51 | PHP_FUNCTION(sp_serialize) { |
| 4 | zif_handler orig_handler; | 52 | zif_handler orig_handler; |
| 5 | 53 | ||
| 6 | /* Call the original `serialize` function. */ | 54 | /* Call the original `serialize` function. */ |
| 7 | orig_handler = | 55 | orig_handler = zend_hash_str_find_ptr(SPG(sp_internal_functions_hook), ZEND_STRL("serialize")); |
| 8 | zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook), | 56 | if (orig_handler) { |
| 9 | "serialize", sizeof("serialize") - 1); | 57 | orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); |
| 10 | orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); | 58 | } |
| 11 | 59 | ||
| 12 | /* Compute the HMAC of the textual representation of the serialized data*/ | 60 | /* Compute the HMAC of the textual representation of the serialized data*/ |
| 13 | zval func_name; | 61 | zend_string *hmac = sp_do_hash_hmac_sha256(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), ZSTR_VAL(SPCFG(encryption_key)), ZSTR_LEN(SPCFG(encryption_key))); |
| 14 | zval hmac; | ||
| 15 | zval params[3]; | ||
| 16 | 62 | ||
| 17 | ZVAL_STRING(&func_name, "hash_hmac"); | 63 | if (!hmac) { |
| 18 | ZVAL_STRING(¶ms[0], "sha256"); | 64 | zend_bailout(); |
| 19 | params[1] = *return_value; | 65 | } |
| 20 | ZVAL_STRING( | ||
| 21 | ¶ms[2], | ||
| 22 | ZSTR_VAL(SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key)); | ||
| 23 | call_user_function(CG(function_table), NULL, &func_name, &hmac, 3, params); | ||
| 24 | 66 | ||
| 25 | size_t len = Z_STRLEN_P(return_value) + Z_STRLEN(hmac); | 67 | size_t len = Z_STRLEN_P(return_value) + ZSTR_LEN(hmac); |
| 26 | if (len < Z_STRLEN_P(return_value)) { | 68 | if (len < Z_STRLEN_P(return_value)) { |
| 27 | // LCOV_EXCL_START | 69 | // LCOV_EXCL_START |
| 28 | sp_log_err("overflow_error", | 70 | sp_log_err("overflow_error", |
| @@ -30,15 +72,11 @@ PHP_FUNCTION(sp_serialize) { | |||
| 30 | zend_bailout(); | 72 | zend_bailout(); |
| 31 | // LCOV_EXCL_STOP | 73 | // LCOV_EXCL_STOP |
| 32 | } | 74 | } |
| 33 | zend_string *res = zend_string_alloc(len, 0); | ||
| 34 | |||
| 35 | memcpy(ZSTR_VAL(res), Z_STRVAL_P(return_value), Z_STRLEN_P(return_value)); | ||
| 36 | memcpy(ZSTR_VAL(res) + Z_STRLEN_P(return_value), Z_STRVAL(hmac), | ||
| 37 | Z_STRLEN(hmac)); | ||
| 38 | 75 | ||
| 39 | /* Append the computed HMAC to the serialized data. */ | 76 | /* Append the computed HMAC to the serialized data. */ |
| 40 | return_value->value.str = res; | 77 | zend_string *orig_ret_str = return_value->value.str; |
| 41 | return; | 78 | RETVAL_NEW_STR(zend_string_concat2(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), ZSTR_VAL(hmac), ZSTR_LEN(hmac))); |
| 79 | zend_string_free(orig_ret_str); | ||
| 42 | } | 80 | } |
| 43 | 81 | ||
| 44 | PHP_FUNCTION(sp_unserialize) { | 82 | PHP_FUNCTION(sp_unserialize) { |
| @@ -47,12 +85,10 @@ PHP_FUNCTION(sp_unserialize) { | |||
| 47 | char *buf = NULL; | 85 | char *buf = NULL; |
| 48 | char *serialized_str = NULL; | 86 | char *serialized_str = NULL; |
| 49 | char *hmac = NULL; | 87 | char *hmac = NULL; |
| 50 | zval expected_hmac; | ||
| 51 | size_t buf_len = 0; | 88 | size_t buf_len = 0; |
| 52 | zval *opts = NULL; | 89 | zval *opts = NULL; |
| 53 | 90 | ||
| 54 | const sp_config_unserialize *config_unserialize = | 91 | const sp_config_unserialize *config_unserialize = &(SPCFG(unserialize)); |
| 55 | SNUFFLEUPAGUS_G(config).config_unserialize; | ||
| 56 | 92 | ||
| 57 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &buf, &buf_len, &opts) == | 93 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &buf, &buf_len, &opts) == |
| 58 | FAILURE) { | 94 | FAILURE) { |
| @@ -68,40 +104,27 @@ PHP_FUNCTION(sp_unserialize) { | |||
| 68 | serialized_str = ecalloc(buf_len - 64 + 1, 1); | 104 | serialized_str = ecalloc(buf_len - 64 + 1, 1); |
| 69 | memcpy(serialized_str, buf, buf_len - 64); | 105 | memcpy(serialized_str, buf, buf_len - 64); |
| 70 | 106 | ||
| 71 | zval func_name; | 107 | zend_string *expected_hmac = sp_do_hash_hmac_sha256(serialized_str, strlen(serialized_str), ZSTR_VAL(SPCFG(encryption_key)), ZSTR_LEN(SPCFG(encryption_key))); |
| 72 | ZVAL_STRING(&func_name, "hash_hmac"); | ||
| 73 | |||
| 74 | zval params[3]; | ||
| 75 | ZVAL_STRING(¶ms[0], "sha256"); | ||
| 76 | ZVAL_STRING(¶ms[1], serialized_str); | ||
| 77 | ZVAL_STRING( | ||
| 78 | ¶ms[2], | ||
| 79 | ZSTR_VAL(SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key)); | ||
| 80 | call_user_function(CG(function_table), NULL, &func_name, &expected_hmac, 3, | ||
| 81 | params); | ||
| 82 | 108 | ||
| 83 | unsigned int status = 0; | 109 | unsigned int status = 0; |
| 84 | for (uint8_t i = 0; i < 64; i++) { | 110 | if (expected_hmac) { |
| 85 | status |= (hmac[i] ^ (Z_STRVAL(expected_hmac))[i]); | 111 | for (uint8_t i = 0; i < 64; i++) { |
| 86 | } | 112 | status |= (hmac[i] ^ (ZSTR_VAL(expected_hmac))[i]); |
| 113 | } | ||
| 114 | } else { status = 1; } | ||
| 87 | 115 | ||
| 88 | if (0 == status) { | 116 | if (0 == status) { |
| 89 | if ((orig_handler = zend_hash_str_find_ptr( | 117 | if ((orig_handler = zend_hash_str_find_ptr(SPG(sp_internal_functions_hook), ZEND_STRL("unserialize")))) { |
| 90 | SNUFFLEUPAGUS_G(sp_internal_functions_hook), "unserialize", | ||
| 91 | sizeof("unserialize") - 1))) { | ||
| 92 | orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); | 118 | orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); |
| 93 | } | 119 | } |
| 94 | } else { | 120 | } else { |
| 95 | if (config_unserialize->dump) { | 121 | if (config_unserialize->dump) { |
| 96 | sp_log_request(config_unserialize->dump, | 122 | sp_log_request(config_unserialize->dump, |
| 97 | config_unserialize->textual_representation, | 123 | config_unserialize->textual_representation); |
| 98 | SP_TOKEN_UNSERIALIZE_HMAC); | ||
| 99 | } | 124 | } |
| 100 | if (true == config_unserialize->simulation) { | 125 | if (true == config_unserialize->simulation) { |
| 101 | sp_log_simulation("unserialize", "Invalid HMAC for %s", serialized_str); | 126 | sp_log_simulation("unserialize", "Invalid HMAC for %s", serialized_str); |
| 102 | if ((orig_handler = zend_hash_str_find_ptr( | 127 | if ((orig_handler = zend_hash_str_find_ptr(SPG(sp_internal_functions_hook), ZEND_STRL("unserialize")))) { |
| 103 | SNUFFLEUPAGUS_G(sp_internal_functions_hook), "unserialize", | ||
| 104 | sizeof("unserialize") - 1))) { | ||
| 105 | orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); | 128 | orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); |
| 106 | } | 129 | } |
| 107 | } else { | 130 | } else { |
