1 #include "php_snuffleupagus.h"
2 
3 #if (HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION))
4 
5 #ifdef ZTS
6 static ts_rsrc_id session_globals_id = 0;
7 #define SESSION_G(v) ZEND_TSRMG(session_globals_id, php_ps_globals *, v)
8 #ifdef COMPILE_DL_SESSION
9 ZEND_TSRMLS_CACHE_EXTERN();
10 #endif
11 #else
12 static php_ps_globals *session_globals = NULL;
13 #define SESSION_G(v) (ps_globals.v)
14 #endif
15 
16 static ps_module *s_module;
17 #if PHP_VERSION_ID < 70300
18 static ps_module *s_original_mod;
19 #else
20 static const ps_module *s_original_mod;
21 #endif
22 static int (*old_s_read)(PS_READ_ARGS);
23 static int (*old_s_write)(PS_WRITE_ARGS);
24 static int (*previous_sessionRINIT)(INIT_FUNC_ARGS) = NULL;
25 static ZEND_INI_MH((*old_OnUpdateSaveHandler)) = NULL;
26 
sp_hook_s_read(PS_READ_ARGS)27 static int sp_hook_s_read(PS_READ_ARGS) {
28   int r = old_s_read(mod_data, key, val, maxlifetime);
29   const sp_config_session *config_session =
30       SNUFFLEUPAGUS_G(config).config_session;
31 
32   if ((NULL == val) || (NULL == *val) || (0 == ZSTR_LEN(*val))) {
33     return r;
34   }
35 
36   if (r == SUCCESS && config_session->encrypt) {
37     zend_string *orig_val = *val;
38     zval val_zval;
39     ZVAL_PSTRINGL(&val_zval, ZSTR_VAL(*val), ZSTR_LEN(*val));
40 
41     int ret = decrypt_zval(&val_zval, config_session->simulation, NULL);
42     if (ZEND_HASH_APPLY_KEEP != ret) {
43       zend_bailout();
44     }
45 
46     *val = zend_string_dup(val_zval.value.str, 0);
47     zend_string_release(orig_val);
48   }
49 
50   return r;
51 }
52 
sp_hook_s_write(PS_WRITE_ARGS)53 static int sp_hook_s_write(PS_WRITE_ARGS) {
54   if (ZSTR_LEN(val) > 0 && SNUFFLEUPAGUS_G(config).config_session->encrypt) {
55     zend_string *new_val = encrypt_zval(val);
56     return old_s_write(mod_data, key, new_val, maxlifetime);
57   }
58   return old_s_write(mod_data, key, val, maxlifetime);
59 }
60 
sp_hook_session_module()61 static void sp_hook_session_module() {
62 #if PHP_VERSION_ID < 70300
63   ps_module *old_mod = SESSION_G(mod);
64 #else
65   const ps_module *old_mod = SESSION_G(mod);
66 #endif
67   ps_module *mod;
68 
69   if (old_mod == NULL || s_module == old_mod) {
70     return;  // LCOV_EXCL_LINE
71   }
72 
73   if (s_module == NULL) {
74     s_module = mod = malloc(sizeof(ps_module));
75     if (mod == NULL) {
76       return;  // LCOV_EXCL_LINE
77     }
78   }
79 
80   s_original_mod = old_mod;
81 
82   mod = s_module;
83   memcpy(mod, old_mod, sizeof(ps_module));
84 
85   old_s_read = mod->s_read;
86   mod->s_read = sp_hook_s_read;
87 
88   old_s_write = mod->s_write;
89   mod->s_write = sp_hook_s_write;
90 
91   SESSION_G(mod) = mod;
92 }
93 
PHP_INI_MH(sp_OnUpdateSaveHandler)94 static PHP_INI_MH(sp_OnUpdateSaveHandler) {
95 #if PHP_VERSION_ID < 70100
96   /* PHP7.0 doesn't handle well recusively set session handlers */
97   if (stage == PHP_INI_STAGE_RUNTIME &&
98       SESSION_G(session_status) == php_session_none && s_original_mod &&
99       zend_string_equals_literal(new_value, "user") == 0 &&
100       strcmp(((ps_module *)s_original_mod)->s_name, "user") == 0) {
101     return SUCCESS;
102   }
103 #endif
104 
105   SESSION_G(mod) = s_original_mod;
106 
107   int r = old_OnUpdateSaveHandler(entry, new_value, mh_arg1, mh_arg2, mh_arg3,
108                                   stage);
109 
110   sp_hook_session_module();
111 
112   return r;
113 }
114 
sp_hook_session_RINIT(INIT_FUNC_ARGS)115 static int sp_hook_session_RINIT(INIT_FUNC_ARGS) {
116   if (SESSION_G(mod) == NULL) {
117     zend_ini_entry *ini_entry;
118     if ((ini_entry = zend_hash_str_find_ptr(
119              EG(ini_directives), ZEND_STRL("session.save_handler")))) {
120       if (ini_entry->value) {
121         sp_OnUpdateSaveHandler(NULL, ini_entry->value, NULL, NULL, NULL, 0);
122       }
123     }
124   }
125   return previous_sessionRINIT(INIT_FUNC_ARGS_PASSTHRU);
126 }
127 
hook_session()128 void hook_session() {
129   zend_module_entry *module;
130 
131   if ((module = zend_hash_str_find_ptr(&module_registry,
132                                        ZEND_STRL("session"))) == NULL) {
133     return;  // LCOV_EXCL_LINE
134   }
135 
136 #ifdef ZTS
137   if (session_globals_id == 0) {
138     session_globals_id = *module->globals_id_ptr;
139   }
140 #else
141   if (session_globals == NULL) {
142     session_globals = module->globals_ptr;
143   }
144 #endif
145   if (old_OnUpdateSaveHandler != NULL) {
146     return;  // LCOV_EXCL_LINE
147   }
148 
149   previous_sessionRINIT = module->request_startup_func;
150   module->request_startup_func = sp_hook_session_RINIT;
151 
152   zend_ini_entry *ini_entry;
153   if ((ini_entry = zend_hash_str_find_ptr(
154            EG(ini_directives), ZEND_STRL("session.save_handler"))) != NULL) {
155     old_OnUpdateSaveHandler = ini_entry->on_modify;
156     ini_entry->on_modify = sp_OnUpdateSaveHandler;
157   }
158   s_module = NULL;
159 
160   sp_hook_session_module();
161 }
162 
163 #else
164 
hook_session()165 void hook_session() {}
166 
167 #endif  // HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
168