From e0c2ca8624ebbf1a2c6ff9cff3d1284236e327e2 Mon Sep 17 00:00:00 2001 From: kkadosh Date: Tue, 15 May 2018 15:08:34 +0000 Subject: Refactor the encryption process to extract encrypt/decrypt functions (#176) Refactor the encryption process to extract encrypt/decrypt functions --- src/config.m4 | 2 +- src/php_snuffleupagus.h | 1 + src/sp_cookie_encryption.c | 123 +--------------------------------------- src/sp_crypt.c | 136 +++++++++++++++++++++++++++++++++++++++++++++ src/sp_crypt.h | 17 ++++++ src/sp_utils.c | 14 ++--- 6 files changed, 163 insertions(+), 130 deletions(-) create mode 100644 src/sp_crypt.c create mode 100644 src/sp_crypt.h (limited to 'src') diff --git a/src/config.m4 b/src/config.m4 index 8d5278e..9909da2 100644 --- a/src/config.m4 +++ b/src/config.m4 @@ -6,7 +6,7 @@ sources="$sources sp_unserialize.c sp_utils.c sp_disable_xxe.c sp_list.c" sources="$sources sp_disabled_functions.c sp_execute.c sp_upload_validation.c" sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c" sources="$sources sp_config_keywords.c sp_var_parser.c sp_var_value.c sp_tree.c" -sources="$sources sp_pcre_compat.c" +sources="$sources sp_pcre_compat.c sp_crypt.c" PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support, [ --enable-snuffleupagus Enable snuffleupagus support]) diff --git a/src/php_snuffleupagus.h b/src/php_snuffleupagus.h index b22d2f1..c658dac 100644 --- a/src/php_snuffleupagus.h +++ b/src/php_snuffleupagus.h @@ -40,6 +40,7 @@ #include "sp_unserialize.h" #include "sp_upload_validation.h" #include "sp_utils.h" +#include "sp_crypt.h" extern zend_module_entry snuffleupagus_module_entry; diff --git a/src/sp_cookie_encryption.c b/src/sp_cookie_encryption.c index 4ecb97d..9030112 100644 --- a/src/sp_cookie_encryption.c +++ b/src/sp_cookie_encryption.c @@ -4,42 +4,6 @@ ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) -static zend_long nonce_d = 0; - -static inline void generate_key(unsigned char *key) { - PHP_SHA256_CTX ctx; - const char *user_agent = getenv("HTTP_USER_AGENT"); - const char *env_var = - getenv(SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var); - const char *encryption_key = - SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key; - - assert(32 == crypto_secretbox_KEYBYTES); // 32 is the size of a SHA256. - assert(encryption_key); // Encryption key can't be NULL - - PHP_SHA256Init(&ctx); - - if (user_agent) { - PHP_SHA256Update(&ctx, (unsigned char *)user_agent, strlen(user_agent)); - } - - if (env_var) { - PHP_SHA256Update(&ctx, (unsigned char *)env_var, strlen(env_var)); - } else { - sp_log_err("cookie_encryption", - "The environment variable '%s'" - "is empty, cookies are weakly encrypted.", - SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var); - } - - if (encryption_key) { - PHP_SHA256Update(&ctx, (const unsigned char *)encryption_key, - strlen(encryption_key)); - } - - PHP_SHA256Final((unsigned char *)key, &ctx); -} - static inline const sp_cookie *sp_lookup_cookie_config(const char *key) { sp_list_node *it = SNUFFLEUPAGUS_G(config).config_cookie->cookies; @@ -56,9 +20,6 @@ static inline const sp_cookie *sp_lookup_cookie_config(const char *key) { /* called at RINIT time with each cookie, eventually decrypt said cookie */ int decrypt_cookie(zval *pDest, int num_args, va_list args, zend_hash_key *hash_key) { - unsigned char key[crypto_secretbox_KEYBYTES] = {0}; - zend_string *debase64; - unsigned char *decrypted; const sp_cookie *cookie = sp_lookup_cookie_config(ZSTR_VAL(hash_key->key)); int ret = 0; @@ -72,57 +33,7 @@ int decrypt_cookie(zval *pDest, int num_args, va_list args, return ZEND_HASH_APPLY_KEEP; } - debase64 = php_base64_decode((unsigned char *)(Z_STRVAL_P(pDest)), - Z_STRLEN_P(pDest)); - - if (ZSTR_LEN(debase64) < - crypto_secretbox_NONCEBYTES + crypto_secretbox_ZEROBYTES) { - if (true == cookie->simulation) { - sp_log_msg( - "cookie_encryption", SP_LOG_SIMULATION, - "Buffer underflow tentative detected in cookie encryption handling " - "for %s. Using the cookie 'as it' instead of decrypting it.", - ZSTR_VAL(hash_key->key)); - return ZEND_HASH_APPLY_KEEP; - } else { - sp_log_msg( - "cookie_encryption", SP_LOG_DROP, - "Buffer underflow tentative detected in cookie encryption handling."); - return ZEND_HASH_APPLY_REMOVE; - } - } - - generate_key(key); - - decrypted = ecalloc(ZSTR_LEN(debase64), 1); - - ret = crypto_secretbox_open( - decrypted, - (unsigned char *)(ZSTR_VAL(debase64) + crypto_secretbox_NONCEBYTES), - ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES, - (unsigned char *)ZSTR_VAL(debase64), key); - - if (-1 == ret) { - if (true == cookie->simulation) { - sp_log_msg( - "cookie_encryption", SP_LOG_SIMULATION, - "Something went wrong with the decryption of %s. Using the cookie " - "'as it' instead of decrypting it", - ZSTR_VAL(hash_key->key)); - return ZEND_HASH_APPLY_KEEP; - } else { - sp_log_msg("cookie_encryption", SP_LOG_DROP, - "Something went wrong with the decryption of %s.", - ZSTR_VAL(hash_key->key)); - return ZEND_HASH_APPLY_REMOVE; - } - } - - ZVAL_STRINGL(pDest, (char *)(decrypted + crypto_secretbox_ZEROBYTES), - ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES - 1 - - crypto_secretbox_ZEROBYTES); - - return ZEND_HASH_APPLY_KEEP; + return decrypt_zval(pDest, cookie->simulation, hash_key); } /* @@ -131,37 +42,7 @@ int decrypt_cookie(zval *pDest, int num_args, va_list args, ** operation). */ static zend_string *encrypt_data(char *data, unsigned long long data_len) { - const size_t encrypted_msg_len = crypto_secretbox_ZEROBYTES + data_len + 1; - const size_t emsg_and_nonce_len = - encrypted_msg_len + crypto_secretbox_NONCEBYTES; - - unsigned char key[crypto_secretbox_KEYBYTES] = {0}; - unsigned char nonce[crypto_secretbox_NONCEBYTES] = {0}; - unsigned char *data_to_encrypt = ecalloc(encrypted_msg_len, 1); - unsigned char *encrypted_data = ecalloc(emsg_and_nonce_len, 1); - - generate_key(key); - - /* tweetnacl's API requires the message to be padded with - crypto_secretbox_ZEROBYTES zeroes. */ - memcpy(data_to_encrypt + crypto_secretbox_ZEROBYTES, data, data_len); - - assert(sizeof(zend_long) <= crypto_secretbox_NONCEBYTES); - - if (0 == nonce_d) { - /* A zend_long should be enough to avoid collisions */ - if (php_random_int_throw(0, ZEND_LONG_MAX, &nonce_d) == FAILURE) { - return NULL; // LCOV_EXCL_LINE - } - } - nonce_d++; - sscanf((char *)nonce, "%ld", &nonce_d); - - memcpy(encrypted_data, nonce, crypto_secretbox_NONCEBYTES); - crypto_secretbox(encrypted_data + crypto_secretbox_NONCEBYTES, - data_to_encrypt, encrypted_msg_len, nonce, key); - - zend_string *z = php_base64_encode(encrypted_data, emsg_and_nonce_len); + zend_string *z = encrypt_zval(data, data_len); sp_log_debug("cookie_encryption", "Cookie value:%s:", z->val); return z; } diff --git a/src/sp_crypt.c b/src/sp_crypt.c new file mode 100644 index 0000000..0c40f1f --- /dev/null +++ b/src/sp_crypt.c @@ -0,0 +1,136 @@ +#include "php_snuffleupagus.h" + +#include "ext/standard/url.h" + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) + +static zend_long nonce_d = 0; + +static void generate_key(unsigned char *key) { + PHP_SHA256_CTX ctx; + const char *user_agent = getenv("HTTP_USER_AGENT"); + const char *env_var = + getenv(SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var); + const char *encryption_key = + SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key; + + assert(32 == crypto_secretbox_KEYBYTES); // 32 is the size of a SHA256. + assert(encryption_key); // Encryption key can't be NULL + + PHP_SHA256Init(&ctx); + + if (user_agent) { + PHP_SHA256Update(&ctx, (unsigned char *)user_agent, strlen(user_agent)); + } + + if (env_var) { + PHP_SHA256Update(&ctx, (unsigned char *)env_var, strlen(env_var)); + } else { + sp_log_err("cookie_encryption", + "The environment variable '%s'" + "is empty, cookies are weakly encrypted.", + SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var); + } + + if (encryption_key) { + PHP_SHA256Update(&ctx, (const unsigned char *)encryption_key, + strlen(encryption_key)); + } + + PHP_SHA256Final((unsigned char *)key, &ctx); +} + +// This function return 0 upon success , non-zero otherwise +int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hash_key) { + unsigned char key[crypto_secretbox_KEYBYTES] = {0}; + unsigned char *decrypted; + zend_string *debase64; + int ret = 0; + + debase64 = php_base64_decode((unsigned char *)(Z_STRVAL_P(pDest)), + Z_STRLEN_P(pDest)); + + if (ZSTR_LEN(debase64) < + crypto_secretbox_NONCEBYTES + crypto_secretbox_ZEROBYTES) { + if (true == simulation) { + sp_log_msg( + "cookie_encryption", SP_LOG_SIMULATION, + "Buffer underflow tentative detected in cookie encryption handling " + "for %s. Using the cookie 'as it' instead of decrypting it.", + ZSTR_VAL(hash_key->key)); + return ZEND_HASH_APPLY_KEEP; + } else { + sp_log_msg( + "cookie_encryption", SP_LOG_DROP, + "Buffer underflow tentative detected in cookie encryption handling."); + return ZEND_HASH_APPLY_REMOVE; + } + } + + generate_key(key); + + decrypted = ecalloc(ZSTR_LEN(debase64), 1); + + ret = crypto_secretbox_open( + decrypted, + (unsigned char *)(ZSTR_VAL(debase64) + crypto_secretbox_NONCEBYTES), + ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES, + (unsigned char *)ZSTR_VAL(debase64), key); + + if (-1 == ret) { + if (true == simulation) { + sp_log_msg( + "cookie_encryption", SP_LOG_SIMULATION, + "Something went wrong with the decryption of %s. Using the cookie " + "'as it' instead of decrypting it", + ZSTR_VAL(hash_key->key)); + return ZEND_HASH_APPLY_KEEP; + } else { + sp_log_msg("cookie_encryption", SP_LOG_DROP, + "Something went wrong with the decryption of %s.", + ZSTR_VAL(hash_key->key)); + return ZEND_HASH_APPLY_REMOVE; + } + } + + ZVAL_STRINGL(pDest, (char *)(decrypted + crypto_secretbox_ZEROBYTES), + ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES - 1 - + crypto_secretbox_ZEROBYTES); + + return ZEND_HASH_APPLY_KEEP; +} + +zend_string *encrypt_zval(char *data, unsigned long long data_len) { + const size_t encrypted_msg_len = crypto_secretbox_ZEROBYTES + data_len + 1; + const size_t emsg_and_nonce_len = + encrypted_msg_len + crypto_secretbox_NONCEBYTES; + + unsigned char key[crypto_secretbox_KEYBYTES] = {0}; + unsigned char nonce[crypto_secretbox_NONCEBYTES] = {0}; + unsigned char *data_to_encrypt = ecalloc(encrypted_msg_len, 1); + unsigned char *encrypted_data = ecalloc(emsg_and_nonce_len, 1); + + generate_key(key); + + /* tweetnacl's API requires the message to be padded with + crypto_secretbox_ZEROBYTES zeroes. */ + memcpy(data_to_encrypt + crypto_secretbox_ZEROBYTES, data, data_len); + + assert(sizeof(zend_long) <= crypto_secretbox_NONCEBYTES); + + if (0 == nonce_d) { + /* A zend_long should be enough to avoid collisions */ + if (php_random_int_throw(0, ZEND_LONG_MAX, &nonce_d) == FAILURE) { + return NULL; // LCOV_EXCL_LINE + } + } + nonce_d++; + sscanf((char *)nonce, "%ld", &nonce_d); + + memcpy(encrypted_data, nonce, crypto_secretbox_NONCEBYTES); + crypto_secretbox(encrypted_data + crypto_secretbox_NONCEBYTES, + data_to_encrypt, encrypted_msg_len, nonce, key); + + zend_string *z = php_base64_encode(encrypted_data, emsg_and_nonce_len); + return z; +} \ No newline at end of file diff --git a/src/sp_crypt.h b/src/sp_crypt.h new file mode 100644 index 0000000..1852a0a --- /dev/null +++ b/src/sp_crypt.h @@ -0,0 +1,17 @@ +#ifndef __SP_CRYPT +#define __SP_CRYPT + +#include "SAPI.h" +#include "tweetnacl.h" + +#include "sp_utils.h" + +#include "ext/hash/php_hash.h" +#include "ext/hash/php_hash_sha.h" +#include "ext/standard/base64.h" + +static void generate_key(unsigned char *key); +int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hask_key); +zend_string *encrypt_zval(char *data, unsigned long long data_len); + +#endif /*__SP_CRYPT */ \ No newline at end of file diff --git a/src/sp_utils.c b/src/sp_utils.c index c0b2fb9..8b7ce49 100644 --- a/src/sp_utils.c +++ b/src/sp_utils.c @@ -241,15 +241,13 @@ void sp_log_disable_ret(const char* restrict path, "Aborted execution on return of the function '%s' in %s:%d, " "because the function returned '%s', which matched the rule '%s'.", path, zend_get_executed_filename(TSRMLS_C), - zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?", - alias); + zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?", alias); } else { - sp_log_msg( - "disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, - "Aborted execution on return of the function '%s' in %s:%d, " - "because the function returned '%s', which matched a rule.", - path, zend_get_executed_filename(TSRMLS_C), - zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?"); + sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, + "Aborted execution on return of the function '%s' in %s:%d, " + "because the function returned '%s', which matched a rule.", + path, zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?"); } if (dump) { sp_log_request(dump, config_node->textual_representation, -- cgit v1.3