1 #include "php_snuffleupagus.h"
2 
PHP_FUNCTION(sp_serialize)3 PHP_FUNCTION(sp_serialize) {
4   zif_handler orig_handler;
5 
6   /* Call the original `serialize` function. */
7   orig_handler =
8       zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook),
9                              "serialize", sizeof("serialize") - 1);
10   orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
11 
12   /* Compute the HMAC of the textual representation of the serialized data*/
13   zval func_name;
14   zval hmac;
15   zval params[3];
16 
17   ZVAL_STRING(&func_name, "hash_hmac");
18   ZVAL_STRING(&params[0], "sha256");
19   params[1] = *return_value;
20   ZVAL_STRING(
21       &params[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 
25   size_t len = Z_STRLEN_P(return_value) + Z_STRLEN(hmac);
26   if (len < Z_STRLEN_P(return_value)) {
27     // LCOV_EXCL_START
28     sp_log_err("overflow_error",
29                "Overflow tentative detected in sp_serialize.");
30     zend_bailout();
31     // LCOV_EXCL_STOP
32   }
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 
39   /* Append the computed HMAC to the serialized data. */
40   return_value->value.str = res;
41   return;
42 }
43 
PHP_FUNCTION(sp_unserialize)44 PHP_FUNCTION(sp_unserialize) {
45   zif_handler orig_handler;
46 
47   char *buf = NULL;
48   char *serialized_str = NULL;
49   char *hmac = NULL;
50   zval expected_hmac;
51   size_t buf_len = 0;
52   zval *opts = NULL;
53 
54   const sp_config_unserialize *config_unserialize =
55       SNUFFLEUPAGUS_G(config).config_unserialize;
56 
57   if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &buf, &buf_len, &opts) ==
58       FAILURE) {
59     RETURN_FALSE;
60   }
61 
62   /* 64 is the length of HMAC-256 */
63   if (buf_len < 64) {
64     sp_log_drop("unserialize", "The serialized object is too small.");
65   }
66 
67   hmac = buf + buf_len - 64;
68   serialized_str = ecalloc(buf_len - 64 + 1, 1);
69   memcpy(serialized_str, buf, buf_len - 64);
70 
71   zval func_name;
72   ZVAL_STRING(&func_name, "hash_hmac");
73 
74   zval params[3];
75   ZVAL_STRING(&params[0], "sha256");
76   ZVAL_STRING(&params[1], serialized_str);
77   ZVAL_STRING(
78       &params[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 
83   unsigned int status = 0;
84   for (uint8_t i = 0; i < 64; i++) {
85     status |= (hmac[i] ^ (Z_STRVAL(expected_hmac))[i]);
86   }
87 
88   if (0 == status) {
89     if ((orig_handler = zend_hash_str_find_ptr(
90              SNUFFLEUPAGUS_G(sp_internal_functions_hook), "unserialize",
91              sizeof("unserialize") - 1))) {
92       orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
93     }
94   } else {
95     if (config_unserialize->dump) {
96       sp_log_request(config_unserialize->dump,
97                      config_unserialize->textual_representation,
98                      SP_TOKEN_UNSERIALIZE_HMAC);
99     }
100     if (true == config_unserialize->simulation) {
101       sp_log_simulation("unserialize", "Invalid HMAC for %s", serialized_str);
102       if ((orig_handler = zend_hash_str_find_ptr(
103                SNUFFLEUPAGUS_G(sp_internal_functions_hook), "unserialize",
104                sizeof("unserialize") - 1))) {
105         orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
106       }
107     } else {
108       sp_log_drop("unserialize", "Invalid HMAC for %s", serialized_str);
109     }
110   }
111   efree(serialized_str);
112   return;
113 }
114 
hook_serialize(void)115 int hook_serialize(void) {
116   TSRMLS_FETCH();
117 
118   HOOK_FUNCTION("serialize", sp_internal_functions_hook, PHP_FN(sp_serialize));
119   HOOK_FUNCTION("unserialize", sp_internal_functions_hook,
120                 PHP_FN(sp_unserialize));
121 
122   return SUCCESS;
123 }
124