summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/config.m42
-rw-r--r--src/php_snuffleupagus.h1
-rw-r--r--src/sp_cookie_encryption.c123
-rw-r--r--src/sp_crypt.c136
-rw-r--r--src/sp_crypt.h17
-rw-r--r--src/sp_utils.c14
6 files changed, 163 insertions, 130 deletions
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"
6sources="$sources sp_disabled_functions.c sp_execute.c sp_upload_validation.c" 6sources="$sources sp_disabled_functions.c sp_execute.c sp_upload_validation.c"
7sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c" 7sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c"
8sources="$sources sp_config_keywords.c sp_var_parser.c sp_var_value.c sp_tree.c" 8sources="$sources sp_config_keywords.c sp_var_parser.c sp_var_value.c sp_tree.c"
9sources="$sources sp_pcre_compat.c" 9sources="$sources sp_pcre_compat.c sp_crypt.c"
10 10
11PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support, 11PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support,
12[ --enable-snuffleupagus Enable snuffleupagus support]) 12[ --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 @@
40#include "sp_unserialize.h" 40#include "sp_unserialize.h"
41#include "sp_upload_validation.h" 41#include "sp_upload_validation.h"
42#include "sp_utils.h" 42#include "sp_utils.h"
43#include "sp_crypt.h"
43 44
44 45
45extern zend_module_entry snuffleupagus_module_entry; 46extern 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 @@
4 4
5ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) 5ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
6 6
7static zend_long nonce_d = 0;
8
9static inline void generate_key(unsigned char *key) {
10 PHP_SHA256_CTX ctx;
11 const char *user_agent = getenv("HTTP_USER_AGENT");
12 const char *env_var =
13 getenv(SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var);
14 const char *encryption_key =
15 SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key;
16
17 assert(32 == crypto_secretbox_KEYBYTES); // 32 is the size of a SHA256.
18 assert(encryption_key); // Encryption key can't be NULL
19
20 PHP_SHA256Init(&ctx);
21
22 if (user_agent) {
23 PHP_SHA256Update(&ctx, (unsigned char *)user_agent, strlen(user_agent));
24 }
25
26 if (env_var) {
27 PHP_SHA256Update(&ctx, (unsigned char *)env_var, strlen(env_var));
28 } else {
29 sp_log_err("cookie_encryption",
30 "The environment variable '%s'"
31 "is empty, cookies are weakly encrypted.",
32 SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var);
33 }
34
35 if (encryption_key) {
36 PHP_SHA256Update(&ctx, (const unsigned char *)encryption_key,
37 strlen(encryption_key));
38 }
39
40 PHP_SHA256Final((unsigned char *)key, &ctx);
41}
42
43static inline const sp_cookie *sp_lookup_cookie_config(const char *key) { 7static inline const sp_cookie *sp_lookup_cookie_config(const char *key) {
44 sp_list_node *it = SNUFFLEUPAGUS_G(config).config_cookie->cookies; 8 sp_list_node *it = SNUFFLEUPAGUS_G(config).config_cookie->cookies;
45 9
@@ -56,9 +20,6 @@ static inline const sp_cookie *sp_lookup_cookie_config(const char *key) {
56/* called at RINIT time with each cookie, eventually decrypt said cookie */ 20/* called at RINIT time with each cookie, eventually decrypt said cookie */
57int decrypt_cookie(zval *pDest, int num_args, va_list args, 21int decrypt_cookie(zval *pDest, int num_args, va_list args,
58 zend_hash_key *hash_key) { 22 zend_hash_key *hash_key) {
59 unsigned char key[crypto_secretbox_KEYBYTES] = {0};
60 zend_string *debase64;
61 unsigned char *decrypted;
62 const sp_cookie *cookie = sp_lookup_cookie_config(ZSTR_VAL(hash_key->key)); 23 const sp_cookie *cookie = sp_lookup_cookie_config(ZSTR_VAL(hash_key->key));
63 int ret = 0; 24 int ret = 0;
64 25
@@ -72,57 +33,7 @@ int decrypt_cookie(zval *pDest, int num_args, va_list args,
72 return ZEND_HASH_APPLY_KEEP; 33 return ZEND_HASH_APPLY_KEEP;
73 } 34 }
74 35
75 debase64 = php_base64_decode((unsigned char *)(Z_STRVAL_P(pDest)), 36 return decrypt_zval(pDest, cookie->simulation, hash_key);
76 Z_STRLEN_P(pDest));
77
78 if (ZSTR_LEN(debase64) <
79 crypto_secretbox_NONCEBYTES + crypto_secretbox_ZEROBYTES) {
80 if (true == cookie->simulation) {
81 sp_log_msg(
82 "cookie_encryption", SP_LOG_SIMULATION,
83 "Buffer underflow tentative detected in cookie encryption handling "
84 "for %s. Using the cookie 'as it' instead of decrypting it.",
85 ZSTR_VAL(hash_key->key));
86 return ZEND_HASH_APPLY_KEEP;
87 } else {
88 sp_log_msg(
89 "cookie_encryption", SP_LOG_DROP,
90 "Buffer underflow tentative detected in cookie encryption handling.");
91 return ZEND_HASH_APPLY_REMOVE;
92 }
93 }
94
95 generate_key(key);
96
97 decrypted = ecalloc(ZSTR_LEN(debase64), 1);
98
99 ret = crypto_secretbox_open(
100 decrypted,
101 (unsigned char *)(ZSTR_VAL(debase64) + crypto_secretbox_NONCEBYTES),
102 ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES,
103 (unsigned char *)ZSTR_VAL(debase64), key);
104
105 if (-1 == ret) {
106 if (true == cookie->simulation) {
107 sp_log_msg(
108 "cookie_encryption", SP_LOG_SIMULATION,
109 "Something went wrong with the decryption of %s. Using the cookie "
110 "'as it' instead of decrypting it",
111 ZSTR_VAL(hash_key->key));
112 return ZEND_HASH_APPLY_KEEP;
113 } else {
114 sp_log_msg("cookie_encryption", SP_LOG_DROP,
115 "Something went wrong with the decryption of %s.",
116 ZSTR_VAL(hash_key->key));
117 return ZEND_HASH_APPLY_REMOVE;
118 }
119 }
120
121 ZVAL_STRINGL(pDest, (char *)(decrypted + crypto_secretbox_ZEROBYTES),
122 ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES - 1 -
123 crypto_secretbox_ZEROBYTES);
124
125 return ZEND_HASH_APPLY_KEEP;
126} 37}
127 38
128/* 39/*
@@ -131,37 +42,7 @@ int decrypt_cookie(zval *pDest, int num_args, va_list args,
131** operation). 42** operation).
132*/ 43*/
133static zend_string *encrypt_data(char *data, unsigned long long data_len) { 44static zend_string *encrypt_data(char *data, unsigned long long data_len) {
134 const size_t encrypted_msg_len = crypto_secretbox_ZEROBYTES + data_len + 1; 45 zend_string *z = encrypt_zval(data, data_len);
135 const size_t emsg_and_nonce_len =
136 encrypted_msg_len + crypto_secretbox_NONCEBYTES;
137
138 unsigned char key[crypto_secretbox_KEYBYTES] = {0};
139 unsigned char nonce[crypto_secretbox_NONCEBYTES] = {0};
140 unsigned char *data_to_encrypt = ecalloc(encrypted_msg_len, 1);
141 unsigned char *encrypted_data = ecalloc(emsg_and_nonce_len, 1);
142
143 generate_key(key);
144
145 /* tweetnacl's API requires the message to be padded with
146 crypto_secretbox_ZEROBYTES zeroes. */
147 memcpy(data_to_encrypt + crypto_secretbox_ZEROBYTES, data, data_len);
148
149 assert(sizeof(zend_long) <= crypto_secretbox_NONCEBYTES);
150
151 if (0 == nonce_d) {
152 /* A zend_long should be enough to avoid collisions */
153 if (php_random_int_throw(0, ZEND_LONG_MAX, &nonce_d) == FAILURE) {
154 return NULL; // LCOV_EXCL_LINE
155 }
156 }
157 nonce_d++;
158 sscanf((char *)nonce, "%ld", &nonce_d);
159
160 memcpy(encrypted_data, nonce, crypto_secretbox_NONCEBYTES);
161 crypto_secretbox(encrypted_data + crypto_secretbox_NONCEBYTES,
162 data_to_encrypt, encrypted_msg_len, nonce, key);
163
164 zend_string *z = php_base64_encode(encrypted_data, emsg_and_nonce_len);
165 sp_log_debug("cookie_encryption", "Cookie value:%s:", z->val); 46 sp_log_debug("cookie_encryption", "Cookie value:%s:", z->val);
166 return z; 47 return z;
167} 48}
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 @@
1#include "php_snuffleupagus.h"
2
3#include "ext/standard/url.h"
4
5ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
6
7static zend_long nonce_d = 0;
8
9static void generate_key(unsigned char *key) {
10 PHP_SHA256_CTX ctx;
11 const char *user_agent = getenv("HTTP_USER_AGENT");
12 const char *env_var =
13 getenv(SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var);
14 const char *encryption_key =
15 SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key;
16
17 assert(32 == crypto_secretbox_KEYBYTES); // 32 is the size of a SHA256.
18 assert(encryption_key); // Encryption key can't be NULL
19
20 PHP_SHA256Init(&ctx);
21
22 if (user_agent) {
23 PHP_SHA256Update(&ctx, (unsigned char *)user_agent, strlen(user_agent));
24 }
25
26 if (env_var) {
27 PHP_SHA256Update(&ctx, (unsigned char *)env_var, strlen(env_var));
28 } else {
29 sp_log_err("cookie_encryption",
30 "The environment variable '%s'"
31 "is empty, cookies are weakly encrypted.",
32 SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var);
33 }
34
35 if (encryption_key) {
36 PHP_SHA256Update(&ctx, (const unsigned char *)encryption_key,
37 strlen(encryption_key));
38 }
39
40 PHP_SHA256Final((unsigned char *)key, &ctx);
41}
42
43// This function return 0 upon success , non-zero otherwise
44int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hash_key) {
45 unsigned char key[crypto_secretbox_KEYBYTES] = {0};
46 unsigned char *decrypted;
47 zend_string *debase64;
48 int ret = 0;
49
50 debase64 = php_base64_decode((unsigned char *)(Z_STRVAL_P(pDest)),
51 Z_STRLEN_P(pDest));
52
53 if (ZSTR_LEN(debase64) <
54 crypto_secretbox_NONCEBYTES + crypto_secretbox_ZEROBYTES) {
55 if (true == simulation) {
56 sp_log_msg(
57 "cookie_encryption", SP_LOG_SIMULATION,
58 "Buffer underflow tentative detected in cookie encryption handling "
59 "for %s. Using the cookie 'as it' instead of decrypting it.",
60 ZSTR_VAL(hash_key->key));
61 return ZEND_HASH_APPLY_KEEP;
62 } else {
63 sp_log_msg(
64 "cookie_encryption", SP_LOG_DROP,
65 "Buffer underflow tentative detected in cookie encryption handling.");
66 return ZEND_HASH_APPLY_REMOVE;
67 }
68 }
69
70 generate_key(key);
71
72 decrypted = ecalloc(ZSTR_LEN(debase64), 1);
73
74 ret = crypto_secretbox_open(
75 decrypted,
76 (unsigned char *)(ZSTR_VAL(debase64) + crypto_secretbox_NONCEBYTES),
77 ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES,
78 (unsigned char *)ZSTR_VAL(debase64), key);
79
80 if (-1 == ret) {
81 if (true == simulation) {
82 sp_log_msg(
83 "cookie_encryption", SP_LOG_SIMULATION,
84 "Something went wrong with the decryption of %s. Using the cookie "
85 "'as it' instead of decrypting it",
86 ZSTR_VAL(hash_key->key));
87 return ZEND_HASH_APPLY_KEEP;
88 } else {
89 sp_log_msg("cookie_encryption", SP_LOG_DROP,
90 "Something went wrong with the decryption of %s.",
91 ZSTR_VAL(hash_key->key));
92 return ZEND_HASH_APPLY_REMOVE;
93 }
94 }
95
96 ZVAL_STRINGL(pDest, (char *)(decrypted + crypto_secretbox_ZEROBYTES),
97 ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES - 1 -
98 crypto_secretbox_ZEROBYTES);
99
100 return ZEND_HASH_APPLY_KEEP;
101}
102
103zend_string *encrypt_zval(char *data, unsigned long long data_len) {
104 const size_t encrypted_msg_len = crypto_secretbox_ZEROBYTES + data_len + 1;
105 const size_t emsg_and_nonce_len =
106 encrypted_msg_len + crypto_secretbox_NONCEBYTES;
107
108 unsigned char key[crypto_secretbox_KEYBYTES] = {0};
109 unsigned char nonce[crypto_secretbox_NONCEBYTES] = {0};
110 unsigned char *data_to_encrypt = ecalloc(encrypted_msg_len, 1);
111 unsigned char *encrypted_data = ecalloc(emsg_and_nonce_len, 1);
112
113 generate_key(key);
114
115 /* tweetnacl's API requires the message to be padded with
116 crypto_secretbox_ZEROBYTES zeroes. */
117 memcpy(data_to_encrypt + crypto_secretbox_ZEROBYTES, data, data_len);
118
119 assert(sizeof(zend_long) <= crypto_secretbox_NONCEBYTES);
120
121 if (0 == nonce_d) {
122 /* A zend_long should be enough to avoid collisions */
123 if (php_random_int_throw(0, ZEND_LONG_MAX, &nonce_d) == FAILURE) {
124 return NULL; // LCOV_EXCL_LINE
125 }
126 }
127 nonce_d++;
128 sscanf((char *)nonce, "%ld", &nonce_d);
129
130 memcpy(encrypted_data, nonce, crypto_secretbox_NONCEBYTES);
131 crypto_secretbox(encrypted_data + crypto_secretbox_NONCEBYTES,
132 data_to_encrypt, encrypted_msg_len, nonce, key);
133
134 zend_string *z = php_base64_encode(encrypted_data, emsg_and_nonce_len);
135 return z;
136} \ 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 @@
1#ifndef __SP_CRYPT
2#define __SP_CRYPT
3
4#include "SAPI.h"
5#include "tweetnacl.h"
6
7#include "sp_utils.h"
8
9#include "ext/hash/php_hash.h"
10#include "ext/hash/php_hash_sha.h"
11#include "ext/standard/base64.h"
12
13static void generate_key(unsigned char *key);
14int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hask_key);
15zend_string *encrypt_zval(char *data, unsigned long long data_len);
16
17#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,
241 "Aborted execution on return of the function '%s' in %s:%d, " 241 "Aborted execution on return of the function '%s' in %s:%d, "
242 "because the function returned '%s', which matched the rule '%s'.", 242 "because the function returned '%s', which matched the rule '%s'.",
243 path, zend_get_executed_filename(TSRMLS_C), 243 path, zend_get_executed_filename(TSRMLS_C),
244 zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?", 244 zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?", alias);
245 alias);
246 } else { 245 } else {
247 sp_log_msg( 246 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
248 "disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 247 "Aborted execution on return of the function '%s' in %s:%d, "
249 "Aborted execution on return of the function '%s' in %s:%d, " 248 "because the function returned '%s', which matched a rule.",
250 "because the function returned '%s', which matched a rule.", 249 path, zend_get_executed_filename(TSRMLS_C),
251 path, zend_get_executed_filename(TSRMLS_C), 250 zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?");
252 zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?");
253 } 251 }
254 if (dump) { 252 if (dump) {
255 sp_log_request(dump, config_node->textual_representation, 253 sp_log_request(dump, config_node->textual_representation,