From 26ee817a0e5a2bed4994fd4efc13e7f5106ca55c Mon Sep 17 00:00:00 2001
From: Ben Fuhrmannek
Date: Sat, 7 Aug 2021 22:33:21 +0200
Subject: PHP7 compatibility
---
src/sp_php_compat.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 src/sp_php_compat.c
(limited to 'src/sp_php_compat.c')
diff --git a/src/sp_php_compat.c b/src/sp_php_compat.c
new file mode 100644
index 0000000..933acd8
--- /dev/null
+++ b/src/sp_php_compat.c
@@ -0,0 +1,22 @@
+#include "php_snuffleupagus.h"
+
+#if PHP_VERSION_ID < 80000
+
+// zend_string_concat2 taken from PHP 8.0.9 zend_string.c
+// TODO: license clarification
+
+ZEND_API zend_string *zend_string_concat2(
+ const char *str1, size_t str1_len,
+ const char *str2, size_t str2_len)
+{
+ size_t len = str1_len + str2_len;
+ zend_string *res = zend_string_alloc(len, 0);
+
+ memcpy(ZSTR_VAL(res), str1, str1_len);
+ memcpy(ZSTR_VAL(res) + str1_len, str2, str2_len);
+ ZSTR_VAL(res)[len] = '\0';
+
+ return res;
+}
+
+#endif
--
cgit v1.3
From ecbc2bba7ba2d1c0c766dd16195ee88edbe550a8 Mon Sep 17 00:00:00 2001
From: Ben Fuhrmannek
Date: Sun, 8 Aug 2021 12:44:13 +0200
Subject: more PHP 7 compatibility and license clarification
---
PHP_LICENSE | 68 +++++++++++++++++++++++++++++++++++++++
doc/source/faq.rst | 6 ++++
src/sp_php_compat.c | 3 +-
src/sp_php_compat.h | 93 +++++++++++++++++++++++++++++++++++++++++++++++++----
4 files changed, 162 insertions(+), 8 deletions(-)
create mode 100644 PHP_LICENSE
(limited to 'src/sp_php_compat.c')
diff --git a/PHP_LICENSE b/PHP_LICENSE
new file mode 100644
index 0000000..4076fe9
--- /dev/null
+++ b/PHP_LICENSE
@@ -0,0 +1,68 @@
+--------------------------------------------------------------------
+ The PHP License, version 3.01
+Copyright (c) 1999 - 2019 The PHP Group. All rights reserved.
+--------------------------------------------------------------------
+
+Redistribution and use in source and binary forms, with or without
+modification, is permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ 3. The name "PHP" must not be used to endorse or promote products
+ derived from this software without prior written permission. For
+ written permission, please contact group@php.net.
+
+ 4. Products derived from this software may not be called "PHP", nor
+ may "PHP" appear in their name, without prior written permission
+ from group@php.net. You may indicate that your software works in
+ conjunction with PHP by saying "Foo for PHP" instead of calling
+ it "PHP Foo" or "phpfoo"
+
+ 5. The PHP Group may publish revised and/or new versions of the
+ license from time to time. Each version will be given a
+ distinguishing version number.
+ Once covered code has been published under a particular version
+ of the license, you may always continue to use it under the terms
+ of that version. You may also choose to use such covered code
+ under the terms of any subsequent version of the license
+ published by the PHP Group. No one other than the PHP Group has
+ the right to modify the terms applicable to covered code created
+ under this License.
+
+ 6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes PHP software, freely available from
+ ".
+
+THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
+ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
+DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+--------------------------------------------------------------------
+
+This software consists of voluntary contributions made by many
+individuals on behalf of the PHP Group.
+
+The PHP Group can be contacted via Email at group@php.net.
+
+For more information on the PHP Group and the PHP project,
+please see .
+
+PHP includes the Zend Engine, freely available at
+.
diff --git a/doc/source/faq.rst b/doc/source/faq.rst
index bdfc7c1..57b910d 100644
--- a/doc/source/faq.rst
+++ b/doc/source/faq.rst
@@ -79,6 +79,12 @@ We chose the LGPL because we don't care that much how you're using Snuffleupagus
but we'd like to force people to make their improvements/contributions
available to everyone.
+The complete license text is shipped with the sources and can be found under ``LICENSE``.
+
+For compatibility with older PHP versions, some original PHP source code was copied or ported back to older versions.
+This source code resides in ``src/sp_php_compat.c`` and ``src/sp_php_compat.h`` and retains its original license
+`The PHP License, version 3.01 `, also included with the sources as ``PHP_LICENSE``.
+
What is the different between SNuffleupaugs and a (WAF) like ModSecurity?
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
diff --git a/src/sp_php_compat.c b/src/sp_php_compat.c
index 933acd8..cd7c3e7 100644
--- a/src/sp_php_compat.c
+++ b/src/sp_php_compat.c
@@ -2,8 +2,7 @@
#if PHP_VERSION_ID < 80000
-// zend_string_concat2 taken from PHP 8.0.9 zend_string.c
-// TODO: license clarification
+// copied from PHP 8.0.9 sources
ZEND_API zend_string *zend_string_concat2(
const char *str1, size_t str1_len,
diff --git a/src/sp_php_compat.h b/src/sp_php_compat.h
index 380abe4..992c3e2 100644
--- a/src/sp_php_compat.h
+++ b/src/sp_php_compat.h
@@ -1,12 +1,93 @@
#if PHP_VERSION_ID < 80000
+
+// copied from PHP 8.0.9 sources
ZEND_API zend_string *zend_string_concat2(
- const char *str1, size_t str1_len,
- const char *str2, size_t str2_len);
+ const char *str1, size_t str1_len,
+ const char *str2, size_t str2_len);
#define ZEND_HASH_REVERSE_FOREACH_KEY_PTR(ht, _h, _key, _ptr) \
- ZEND_HASH_REVERSE_FOREACH(ht, 0); \
- _h = _p->h; \
- _key = _p->key; \
- _ptr = Z_PTR_P(_z);
+ ZEND_HASH_REVERSE_FOREACH(ht, 0); \
+ _h = _p->h; \
+ _key = _p->key; \
+ _ptr = Z_PTR_P(_z);
+
+#endif
+
+#if PHP_VERSION_ID < 70300
+
+// copied from PHP 7.4.22 sources
+
+static zend_always_inline uint32_t zend_gc_delref(zend_refcounted_h *p) {
+ ZEND_ASSERT(p->refcount > 0);
+ // ZEND_RC_MOD_CHECK(p);
+ return --(p->refcount);
+}
+#define GC_DELREF(p) zend_gc_delref(&(p)->gc)
+
+static zend_always_inline void zend_string_release_ex(zend_string *s, int persistent)
+{
+ if (!ZSTR_IS_INTERNED(s)) {
+ if (GC_DELREF(s) == 0) {
+ if (persistent) {
+ ZEND_ASSERT(GC_FLAGS(s) & IS_STR_PERSISTENT);
+ free(s);
+ } else {
+ ZEND_ASSERT(!(GC_FLAGS(s) & IS_STR_PERSISTENT));
+ efree(s);
+ }
+ }
+ }
+}
+
+static zend_always_inline void zend_string_efree(zend_string *s)
+{
+ ZEND_ASSERT(!ZSTR_IS_INTERNED(s));
+ ZEND_ASSERT(GC_REFCOUNT(s) <= 1);
+ ZEND_ASSERT(!(GC_FLAGS(s) & IS_STR_PERSISTENT));
+ efree(s);
+}
+
+#endif
+
+#if PHP_VERSION_ID < 70200
+
+#undef ZEND_HASH_REVERSE_FOREACH
+
+// copied from PHP 7.4.22 sources
+
+#define ZEND_HASH_REVERSE_FOREACH(_ht, indirect) do { \
+ HashTable *__ht = (_ht); \
+ uint32_t _idx = __ht->nNumUsed; \
+ Bucket *_p = __ht->arData + _idx; \
+ zval *_z; \
+ for (_idx = __ht->nNumUsed; _idx > 0; _idx--) { \
+ _p--; \
+ _z = &_p->val; \
+ if (indirect && Z_TYPE_P(_z) == IS_INDIRECT) { \
+ _z = Z_INDIRECT_P(_z); \
+ } \
+ if (UNEXPECTED(Z_TYPE_P(_z) == IS_UNDEF)) continue;
+
+
+#define ZEND_HASH_FOREACH_END_DEL() \
+ __ht->nNumOfElements--; \
+ do { \
+ uint32_t j = HT_IDX_TO_HASH(_idx - 1); \
+ uint32_t nIndex = _p->h | __ht->nTableMask; \
+ uint32_t i = HT_HASH(__ht, nIndex); \
+ if (UNEXPECTED(j != i)) { \
+ Bucket *prev = HT_HASH_TO_BUCKET(__ht, i); \
+ while (Z_NEXT(prev->val) != j) { \
+ i = Z_NEXT(prev->val); \
+ prev = HT_HASH_TO_BUCKET(__ht, i); \
+ } \
+ Z_NEXT(prev->val) = Z_NEXT(_p->val); \
+ } else { \
+ HT_HASH(__ht, nIndex) = Z_NEXT(_p->val); \
+ } \
+ } while (0); \
+ } \
+ __ht->nNumUsed = _idx; \
+ } while (0)
#endif
\ No newline at end of file
--
cgit v1.3
From c447df6ce8964b2863a50f0f8027d9b234b7507f Mon Sep 17 00:00:00 2001
From: Ben Fuhrmannek
Date: Fri, 19 Nov 2021 14:57:01 +0100
Subject: replaced call_user_func with C level call
---
src/sp_php_compat.c | 4 +++
src/sp_php_compat.h | 36 ++++++++++++++++++++++-
src/sp_unserialize.c | 81 +++++++++++++++++++++++++++++++++-------------------
3 files changed, 90 insertions(+), 31 deletions(-)
(limited to 'src/sp_php_compat.c')
diff --git a/src/sp_php_compat.c b/src/sp_php_compat.c
index cd7c3e7..a0693c3 100644
--- a/src/sp_php_compat.c
+++ b/src/sp_php_compat.c
@@ -1,5 +1,9 @@
#include "php_snuffleupagus.h"
+/* code in this file is licensed under its original license
+ The PHP License, version 3.01 (https://www.php.net/license/3_01.txt)
+ which is also included with these sources in the file `PHP_LICENSE` */
+
#if PHP_VERSION_ID < 80000
// copied from PHP 8.0.9 sources
diff --git a/src/sp_php_compat.h b/src/sp_php_compat.h
index 09d9a1f..d1102a8 100644
--- a/src/sp_php_compat.h
+++ b/src/sp_php_compat.h
@@ -1,3 +1,7 @@
+/* code in this file is licensed under its original license
+The PHP License, version 3.01 (https://www.php.net/license/3_01.txt)
+which is also included with these sources in the file `PHP_LICENSE` */
+
#if PHP_VERSION_ID < 80000
// copied from PHP 8.0.9 sources
@@ -93,4 +97,34 @@ static zend_always_inline void zend_string_efree(zend_string *s)
__ht->nNumUsed = _idx; \
} while (0)
-#endif
\ No newline at end of file
+#endif
+
+// copied from PHP 8.0.11 sources, ext/hash/hash.c
+
+static inline void php_hash_string_xor_char(unsigned char *out, const unsigned char *in, const unsigned char xor_with, const size_t length) {
+ size_t i;
+ for (i=0; i < length; i++) {
+ out[i] = in[i] ^ xor_with;
+ }
+}
+
+static inline void php_hash_hmac_prep_key(unsigned char *K, const php_hash_ops *ops, void *context, const unsigned char *key, const size_t key_len) {
+ memset(K, 0, ops->block_size);
+ if (key_len > ops->block_size) {
+ /* Reduce the key first */
+ ops->hash_init(context);
+ ops->hash_update(context, key, key_len);
+ ops->hash_final(K, context);
+ } else {
+ memcpy(K, key, key_len);
+ }
+ /* XOR the key with 0x36 to get the ipad) */
+ php_hash_string_xor_char(K, K, 0x36, ops->block_size);
+}
+
+static inline void php_hash_hmac_round(unsigned char *final, const php_hash_ops *ops, void *context, const unsigned char *key, const unsigned char *data, const zend_long data_size) {
+ ops->hash_init(context);
+ ops->hash_update(context, key, ops->block_size);
+ ops->hash_update(context, data, data_size);
+ ops->hash_final(final, context);
+}
diff --git a/src/sp_unserialize.c b/src/sp_unserialize.c
index 5ede015..4a9f565 100644
--- a/src/sp_unserialize.c
+++ b/src/sp_unserialize.c
@@ -1,5 +1,40 @@
#include "php_snuffleupagus.h"
+// condensed version of PHP's php_hash_do_hash_hmac() in ext/hash/hash.c
+static zend_string *sp_do_hash_hmac_sha256(char *data, size_t data_len, char *key, size_t key_len)
+{
+ zend_string *algo = zend_string_init(ZEND_STRL("sha256"), 0);
+ const php_hash_ops *ops = php_hash_fetch_ops(algo);
+ zend_string_release_ex(algo, 0);
+
+ if (!ops || !ops->is_crypto) {
+ sp_log_err("unsupported hash algorithm for hmac: %s", ZSTR_VAL(algo));
+ return NULL;
+ }
+
+ void *context = php_hash_alloc_context(ops);
+
+ unsigned char *K = emalloc(ops->block_size);
+ zend_string *digest = zend_string_alloc(ops->digest_size, 0);
+
+ php_hash_hmac_prep_key(K, ops, context, (unsigned char *) key, key_len);
+ php_hash_hmac_round((unsigned char *) ZSTR_VAL(digest), ops, context, K, (unsigned char *) data, data_len);
+ php_hash_string_xor_char(K, K, 0x6A, ops->block_size);
+ php_hash_hmac_round((unsigned char *) ZSTR_VAL(digest), ops, context, K, (unsigned char *) ZSTR_VAL(digest), ops->digest_size);
+
+ /* Zero the key */
+ ZEND_SECURE_ZERO(K, ops->block_size);
+ efree(K);
+ efree(context);
+
+ zend_string *hex_digest = zend_string_safe_alloc(ops->digest_size, 2, 0, 0);
+
+ php_hash_bin2hex(ZSTR_VAL(hex_digest), (unsigned char *) ZSTR_VAL(digest), ops->digest_size);
+ ZSTR_VAL(hex_digest)[2 * ops->digest_size] = 0;
+ zend_string_release_ex(digest, 0);
+ return hex_digest;
+}
+
PHP_FUNCTION(sp_serialize) {
zif_handler orig_handler;
@@ -10,19 +45,13 @@ PHP_FUNCTION(sp_serialize) {
}
/* Compute the HMAC of the textual representation of the serialized data*/
- zval func_name;
- zval hmac;
- zval params[3] = {0};
-
- ZVAL_STRING(&func_name, "hash_hmac");
- ZVAL_STRING(¶ms[0], "sha256");
- params[1] = *return_value;
- ZVAL_STRING(
- ¶ms[2],
- ZSTR_VAL(SPCFG(encryption_key)));
- call_user_function(CG(function_table), NULL, &func_name, &hmac, 3, params);
-
- size_t len = Z_STRLEN_P(return_value) + Z_STRLEN(hmac);
+ 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)));
+
+ if (!hmac) {
+ zend_bailout();
+ }
+
+ size_t len = Z_STRLEN_P(return_value) + ZSTR_LEN(hmac);
if (len < Z_STRLEN_P(return_value)) {
// LCOV_EXCL_START
sp_log_err("overflow_error",
@@ -32,8 +61,9 @@ PHP_FUNCTION(sp_serialize) {
}
/* Append the computed HMAC to the serialized data. */
- return_value->value.str = zend_string_concat2(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), Z_STRVAL(hmac), Z_STRLEN(hmac));
- return;
+ zend_string *orig_ret_str = return_value->value.str;
+ RETVAL_NEW_STR(zend_string_concat2(Z_STRVAL_P(return_value), Z_STRLEN_P(return_value), ZSTR_VAL(hmac), ZSTR_LEN(hmac)));
+ zend_string_free(orig_ret_str);
}
PHP_FUNCTION(sp_unserialize) {
@@ -42,7 +72,6 @@ PHP_FUNCTION(sp_unserialize) {
char *buf = NULL;
char *serialized_str = NULL;
char *hmac = NULL;
- zval expected_hmac;
size_t buf_len = 0;
zval *opts = NULL;
@@ -62,22 +91,14 @@ PHP_FUNCTION(sp_unserialize) {
serialized_str = ecalloc(buf_len - 64 + 1, 1);
memcpy(serialized_str, buf, buf_len - 64);
- zval func_name;
- ZVAL_STRING(&func_name, "hash_hmac");
-
- zval params[3] = {0};
- ZVAL_STRING(¶ms[0], "sha256");
- ZVAL_STRING(¶ms[1], serialized_str);
- ZVAL_STRING(
- ¶ms[2],
- ZSTR_VAL(SPCFG(encryption_key)));
- call_user_function(CG(function_table), NULL, &func_name, &expected_hmac, 3,
- params);
+ zend_string *expected_hmac = sp_do_hash_hmac_sha256(serialized_str, strlen(serialized_str), ZSTR_VAL(SPCFG(encryption_key)), ZSTR_LEN(SPCFG(encryption_key)));
unsigned int status = 0;
- for (uint8_t i = 0; i < 64; i++) {
- status |= (hmac[i] ^ (Z_STRVAL(expected_hmac))[i]);
- }
+ if (expected_hmac) {
+ for (uint8_t i = 0; i < 64; i++) {
+ status |= (hmac[i] ^ (ZSTR_VAL(expected_hmac))[i]);
+ }
+ } else { status = 1; }
if (0 == status) {
if ((orig_handler = zend_hash_str_find_ptr(SPG(sp_internal_functions_hook), ZEND_STRL("unserialize")))) {
--
cgit v1.3