summaryrefslogtreecommitdiff
path: root/src/sp_wrapper.c
diff options
context:
space:
mode:
authorChristian Göttsche2024-05-27 21:33:00 +0200
committerjvoisin2024-06-09 17:16:16 +0200
commitc7ce5c3528e8da8762e6e7067001549e109397ba (patch)
tree4c9606730af25a8f893193b7cc5cb718a20c3f35 /src/sp_wrapper.c
parent849252c6a48b428dde3ad8930b40a2bdf9874cb7 (diff)
Add option to specify the allowed "php" wrapper types
In addition of the current possibility to filter wrappers by their protocol name, also add the option to filter the "php" wrapper by the requested kind. Especially the 'filter' backend can be disabled that way.
Diffstat (limited to 'src/sp_wrapper.c')
-rw-r--r--src/sp_wrapper.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/src/sp_wrapper.c b/src/sp_wrapper.c
index 9eb5cbc..54a3a7a 100644
--- a/src/sp_wrapper.c
+++ b/src/sp_wrapper.c
@@ -1,5 +1,7 @@
1#include "php_snuffleupagus.h" 1#include "php_snuffleupagus.h"
2 2
3#define LOG_FEATURE "wrappers_whitelist"
4
3static bool wrapper_is_whitelisted(const zend_string *const zs) { 5static bool wrapper_is_whitelisted(const zend_string *const zs) {
4 const sp_list_node *list = SPCFG(wrapper).whitelist; 6 const sp_list_node *list = SPCFG(wrapper).whitelist;
5 7
@@ -16,6 +18,131 @@ static bool wrapper_is_whitelisted(const zend_string *const zs) {
16 return false; 18 return false;
17} 19}
18 20
21static bool sp_php_stream_is_filtered(void) {
22 const sp_list_node *list = SPCFG(wrapper).php_stream_allowlist;
23
24 return list != NULL;
25}
26
27static bool sp_php_stream_is_whitelisted(const char *const kind) {
28 const sp_list_node *list = SPCFG(wrapper).php_stream_allowlist;
29
30 while (list) {
31 if (!strcasecmp(kind, ZSTR_VAL((const zend_string *)list->data))) {
32 return true;
33 }
34 list = list->next;
35 }
36 return false;
37}
38
39/*
40 * Adopted from
41 * https://github.com/php/php-src/blob/8896bd3200892000d8aaa01595d6c64b926a26f7/ext/standard/php_fopen_wrapper.c#L176
42 */
43static php_stream * sp_php_stream_url_wrap_php(php_stream_wrapper *wrapper,
44 const char *path, const char *mode,
45 int options, zend_string **opened_path,
46 php_stream_context *context STREAMS_DC) {
47 if (!strncasecmp(path, "php://", 6)) {
48 path += 6;
49 }
50
51 if (!strncasecmp(path, "temp", 4)) {
52 if (!sp_php_stream_is_whitelisted("temp")) {
53 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"temp\" dropped");
54 return NULL;
55 }
56 } else if (!strcasecmp(path, "memory")) {
57 if (!sp_php_stream_is_whitelisted("memory")) {
58 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"memory\" dropped");
59 return NULL;
60 }
61 } else if (!strcasecmp(path, "output")) {
62 if (!sp_php_stream_is_whitelisted("output")) {
63 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"output\" dropped");
64 return NULL;
65 }
66 } else if (!strcasecmp(path, "input")) {
67 if (!sp_php_stream_is_whitelisted("input")) {
68 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"input\" dropped");
69 return NULL;
70 }
71 } else if (!strcasecmp(path, "stdin")) {
72 if (!sp_php_stream_is_whitelisted("stdin")) {
73 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"stdin\" dropped");
74 return NULL;
75 }
76 } else if (!strcasecmp(path, "stdout")) {
77 if (!sp_php_stream_is_whitelisted("stdout")) {
78 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"stdout\" dropped");
79 return NULL;
80 }
81 } else if (!strcasecmp(path, "stderr")) {
82 if (!sp_php_stream_is_whitelisted("stderr")) {
83 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"stderr\" dropped");
84 return NULL;
85 }
86 } else if (!strncasecmp(path, "fd/", 3)) {
87 if (!sp_php_stream_is_whitelisted("fd")) {
88 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"fd\" dropped");
89 return NULL;
90 }
91 } else if (!strncasecmp(path, "filter/", 7)) {
92 if (!sp_php_stream_is_whitelisted("filter")) {
93 sp_log_warn(LOG_FEATURE, "Call to not allowed php stream type \"filter\" dropped");
94 return NULL;
95 }
96 } else {
97 sp_log_warn(LOG_FEATURE, "Call to unknown php stream type dropped");
98 return NULL;
99 }
100
101 extern PHPAPI const php_stream_wrapper php_stream_php_wrapper;
102
103 return php_stream_php_wrapper.wops->stream_opener(wrapper, path, mode, options, opened_path, context STREAMS_DC);
104}
105
106/*
107 * Adopted from
108 * https://github.com/php/php-src/blob/8896bd3200892000d8aaa01595d6c64b926a26f7/ext/standard/php_fopen_wrapper.c#L428-L446
109 */
110static const php_stream_wrapper_ops sp_php_stdio_wops = {
111 sp_php_stream_url_wrap_php,
112 NULL, /* close */
113 NULL, /* fstat */
114 NULL, /* stat */
115 NULL, /* opendir */
116 "PHP",
117 NULL, /* unlink */
118 NULL, /* rename */
119 NULL, /* mkdir */
120 NULL, /* rmdir */
121 NULL
122};
123static const php_stream_wrapper sp_php_stream_php_wrapper = {
124 &sp_php_stdio_wops,
125 NULL,
126 0, /* is_url */
127};
128
129static void sp_reregister_php_wrapper(void) {
130 if (!sp_php_stream_is_filtered()) {
131 return;
132 }
133
134 if (php_unregister_url_stream_wrapper("php") != SUCCESS) {
135 sp_log_warn(LOG_FEATURE, "Failed to unregister stream wrapper \"php\"");
136 return;
137 }
138
139 if (php_register_url_stream_wrapper("php", &sp_php_stream_php_wrapper) != SUCCESS) {
140 sp_log_warn(LOG_FEATURE, "Failed to register custom stream wrapper \"php\"");
141 }
142
143 sp_log_debug(LOG_FEATURE, "Stream \"php\" successfully re-registered");
144}
145
19void sp_disable_wrapper() { 146void sp_disable_wrapper() {
20 HashTable *orig = php_stream_get_url_stream_wrappers_hash(); 147 HashTable *orig = php_stream_get_url_stream_wrappers_hash();
21 HashTable *orig_complete = pemalloc(sizeof(HashTable), 1); 148 HashTable *orig_complete = pemalloc(sizeof(HashTable), 1);
@@ -50,6 +177,12 @@ PHP_FUNCTION(sp_stream_wrapper_register) {
50 zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "S*", &protocol_name, &params, &param_count); 177 zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "S*", &protocol_name, &params, &param_count);
51 // ignore proper arguments here and just let the original handler deal with it 178 // ignore proper arguments here and just let the original handler deal with it
52 if (!protocol_name || wrapper_is_whitelisted(protocol_name)) { 179 if (!protocol_name || wrapper_is_whitelisted(protocol_name)) {
180
181 // reject manual loading of "php" wrapper
182 if (!strcasecmp(ZSTR_VAL(protocol_name), "php") && sp_php_stream_is_filtered()) {
183 return;
184 }
185
53 orig_handler = zend_hash_str_find_ptr(SPG(sp_internal_functions_hook), ZEND_STRL("stream_wrapper_register")); 186 orig_handler = zend_hash_str_find_ptr(SPG(sp_internal_functions_hook), ZEND_STRL("stream_wrapper_register"));
54 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); 187 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
55 } 188 }
@@ -61,5 +194,7 @@ int hook_stream_wrappers() {
61 HOOK_FUNCTION("stream_wrapper_register", sp_internal_functions_hook, 194 HOOK_FUNCTION("stream_wrapper_register", sp_internal_functions_hook,
62 PHP_FN(sp_stream_wrapper_register)); 195 PHP_FN(sp_stream_wrapper_register));
63 196
197 sp_reregister_php_wrapper();
198
64 return SUCCESS; 199 return SUCCESS;
65} 200}