diff options
| -rw-r--r-- | src/php_snuffleupagus.h | 1 | ||||
| -rw-r--r-- | src/sp_ini.c | 60 | ||||
| -rw-r--r-- | src/tests/ini/ini_min_policy_drop.phpt | 2 | ||||
| -rw-r--r-- | src/tests/ini/ini_minmax.phpt | 4 | ||||
| -rw-r--r-- | src/tests/ini/ini_null.phpt | 2 | ||||
| -rw-r--r-- | src/tests/ini/ini_regexp.phpt | 2 | ||||
| -rw-r--r-- | src/tests/ini/ini_regexp_drop.phpt | 2 | ||||
| -rw-r--r-- | src/tests/ini/ini_regexp_drop_base64.phpt | 13 |
8 files changed, 75 insertions, 11 deletions
diff --git a/src/php_snuffleupagus.h b/src/php_snuffleupagus.h index 53cf1da..6e035d4 100644 --- a/src/php_snuffleupagus.h +++ b/src/php_snuffleupagus.h | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <glob.h> | 17 | #include <glob.h> |
| 18 | #endif | 18 | #endif |
| 19 | 19 | ||
| 20 | #include <ctype.h> | ||
| 20 | #include <errno.h> | 21 | #include <errno.h> |
| 21 | #include <fcntl.h> | 22 | #include <fcntl.h> |
| 22 | #include <inttypes.h> | 23 | #include <inttypes.h> |
diff --git a/src/sp_ini.c b/src/sp_ini.c index 7b22012..8860a92 100644 --- a/src/sp_ini.c +++ b/src/sp_ini.c | |||
| @@ -11,6 +11,29 @@ | |||
| 11 | sp_log_auto2("ini_protection", simulation, (cfg->policy_drop || (entry && entry->drop)), __VA_ARGS__); \ | 11 | sp_log_auto2("ini_protection", simulation, (cfg->policy_drop || (entry && entry->drop)), __VA_ARGS__); \ |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | static const char* check_safe_key(const char* string) { | ||
| 15 | for (const char* p = string; *p != '\0'; p++) { | ||
| 16 | if (!isalnum((unsigned char)*p) && *p != '_' && *p != '.') { | ||
| 17 | return NULL; | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | return string; | ||
| 22 | } | ||
| 23 | |||
| 24 | static const char* check_safe_value(const char* string) { | ||
| 25 | for (const char* p = string; *p != '\0'; p++) { | ||
| 26 | if (*p == '\'' || *p == '"') { | ||
| 27 | return NULL; | ||
| 28 | } | ||
| 29 | |||
| 30 | if (!isgraph((unsigned char)*p)) { | ||
| 31 | return NULL; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | return string; | ||
| 36 | } | ||
| 14 | 37 | ||
| 15 | static bool /* success */ sp_ini_check(zend_string *const restrict varname, zend_string const *const restrict new_value, sp_ini_entry **sp_entry_p) { | 38 | static bool /* success */ sp_ini_check(zend_string *const restrict varname, zend_string const *const restrict new_value, sp_ini_entry **sp_entry_p) { |
| 16 | if (!varname || ZSTR_LEN(varname) == 0) { | 39 | if (!varname || ZSTR_LEN(varname) == 0) { |
| @@ -27,7 +50,8 @@ static bool /* success */ sp_ini_check(zend_string *const restrict varname, zend | |||
| 27 | if (!entry) { | 50 | if (!entry) { |
| 28 | if (cfg->policy_readonly) { | 51 | if (cfg->policy_readonly) { |
| 29 | if (!cfg->policy_silent_ro) { | 52 | if (!cfg->policy_silent_ro) { |
| 30 | sp_log_ini_check_violation("INI setting is read-only"); | 53 | sp_log_ini_check_violation("INI setting `%s` is read-only", |
| 54 | check_safe_key(ZSTR_VAL(varname)) ? ZSTR_VAL(varname) : "<invalid>"); | ||
| 31 | } | 55 | } |
| 32 | return simulation; | 56 | return simulation; |
| 33 | } | 57 | } |
| @@ -38,7 +62,11 @@ static bool /* success */ sp_ini_check(zend_string *const restrict varname, zend | |||
| 38 | 62 | ||
| 39 | if (SP_INI_ACCESS_READONLY_COND(entry, cfg)) { | 63 | if (SP_INI_ACCESS_READONLY_COND(entry, cfg)) { |
| 40 | if (!cfg->policy_silent_ro) { | 64 | if (!cfg->policy_silent_ro) { |
| 41 | sp_log_ini_check_violation("%s", (entry->msg ? ZSTR_VAL(entry->msg) : "INI setting is read-only")); | 65 | if (entry->msg) { |
| 66 | sp_log_ini_check_violation("%s", ZSTR_VAL(entry->msg)); | ||
| 67 | } else { | ||
| 68 | sp_log_ini_check_violation("INI setting `%s` is read-only", ZSTR_VAL(entry->key)); | ||
| 69 | } | ||
| 42 | } | 70 | } |
| 43 | return simulation; | 71 | return simulation; |
| 44 | } | 72 | } |
| @@ -48,7 +76,7 @@ static bool /* success */ sp_ini_check(zend_string *const restrict varname, zend | |||
| 48 | return true; // allow NULL value and skip other tests | 76 | return true; // allow NULL value and skip other tests |
| 49 | } | 77 | } |
| 50 | if (SP_INI_HAS_CHECKS_COND(entry)) { | 78 | if (SP_INI_HAS_CHECKS_COND(entry)) { |
| 51 | sp_log_ini_check_violation("new INI value must not be NULL or empty"); | 79 | sp_log_ini_check_violation("new INI value for `%s` must not be NULL or empty", ZSTR_VAL(entry->key)); |
| 52 | return simulation; | 80 | return simulation; |
| 53 | } | 81 | } |
| 54 | return true; // no new_value, but no checks to perform | 82 | return true; // no new_value, but no checks to perform |
| @@ -66,14 +94,36 @@ static bool /* success */ sp_ini_check(zend_string *const restrict varname, zend | |||
| 66 | if ((entry->min && zend_atol(ZSTR_VAL(entry->min), ZSTR_LEN(entry->min)) > lvalue) || | 94 | if ((entry->min && zend_atol(ZSTR_VAL(entry->min), ZSTR_LEN(entry->min)) > lvalue) || |
| 67 | (entry->max && zend_atol(ZSTR_VAL(entry->max), ZSTR_LEN(entry->max)) < lvalue)) { | 95 | (entry->max && zend_atol(ZSTR_VAL(entry->max), ZSTR_LEN(entry->max)) < lvalue)) { |
| 68 | #endif | 96 | #endif |
| 69 | sp_log_ini_check_violation("%s", (entry->msg ? ZSTR_VAL(entry->msg) : "INI value out of range")); | 97 | if (entry->msg) { |
| 98 | sp_log_ini_check_violation("%s", ZSTR_VAL(entry->msg)); | ||
| 99 | } else { | ||
| 100 | sp_log_ini_check_violation("INI value %lld for `%s` out of range", lvalue, ZSTR_VAL(entry->key)); | ||
| 101 | } | ||
| 70 | return simulation; | 102 | return simulation; |
| 71 | } | 103 | } |
| 72 | } | 104 | } |
| 73 | 105 | ||
| 74 | if (entry->regexp) { | 106 | if (entry->regexp) { |
| 75 | if (!sp_is_regexp_matching_zstr(entry->regexp, new_value)) { | 107 | if (!sp_is_regexp_matching_zstr(entry->regexp, new_value)) { |
| 76 | sp_log_ini_check_violation("%s", (entry->msg ? ZSTR_VAL(entry->msg) : "INI value does not match regex")); | 108 | if (entry->msg) { |
| 109 | sp_log_ini_check_violation("%s", ZSTR_VAL(entry->msg)); | ||
| 110 | } else { | ||
| 111 | zend_string *base64_new_val = NULL; | ||
| 112 | const char *safe_new_val = check_safe_value(ZSTR_VAL(new_value)); | ||
| 113 | if (!safe_new_val) { | ||
| 114 | base64_new_val = php_base64_encode((const unsigned char*)(ZSTR_VAL(new_value)), ZSTR_LEN(new_value)); | ||
| 115 | safe_new_val = base64_new_val ? ZSTR_VAL(base64_new_val) : "<invalid>"; | ||
| 116 | } | ||
| 117 | |||
| 118 | sp_log_ini_check_violation("INI value `%s`%s for `%s` does not match regex", | ||
| 119 | safe_new_val, | ||
| 120 | base64_new_val ? "(base64)" : "", | ||
| 121 | ZSTR_VAL(entry->key)); | ||
| 122 | |||
| 123 | if (base64_new_val) { | ||
| 124 | zend_string_release(base64_new_val); | ||
| 125 | } | ||
| 126 | } | ||
| 77 | return simulation; | 127 | return simulation; |
| 78 | } | 128 | } |
| 79 | } | 129 | } |
diff --git a/src/tests/ini/ini_min_policy_drop.phpt b/src/tests/ini/ini_min_policy_drop.phpt index 1ec9f9a..43e180e 100644 --- a/src/tests/ini/ini_min_policy_drop.phpt +++ b/src/tests/ini/ini_min_policy_drop.phpt | |||
| @@ -10,4 +10,4 @@ var_dump(ini_set("max_execution_time", "29") === false); | |||
| 10 | var_dump(ini_get("max_execution_time")); | 10 | var_dump(ini_get("max_execution_time")); |
| 11 | ?> | 11 | ?> |
| 12 | --EXPECTF-- | 12 | --EXPECTF-- |
| 13 | Fatal error: [snuffleupagus][0.0.0.0][ini_protection][drop] INI value out of range in %a/ini_min_policy_drop.php on line 2%A | 13 | Fatal error: [snuffleupagus][0.0.0.0][ini_protection][drop] INI value 29 for `max_execution_time` out of range in %a/ini_min_policy_drop.php on line 2%A |
diff --git a/src/tests/ini/ini_minmax.phpt b/src/tests/ini/ini_minmax.phpt index facb73e..10c15a4 100644 --- a/src/tests/ini/ini_minmax.phpt +++ b/src/tests/ini/ini_minmax.phpt | |||
| @@ -25,10 +25,10 @@ string(2) "30" | |||
| 25 | bool(false) | 25 | bool(false) |
| 26 | string(3) "300" | 26 | string(3) "300" |
| 27 | 27 | ||
| 28 | Warning: [snuffleupagus][0.0.0.0][ini_protection][log] INI value out of range in %a/ini_minmax.php on line 8 | 28 | Warning: [snuffleupagus][0.0.0.0][ini_protection][log] INI value 29 for `max_execution_time` out of range in %a/ini_minmax.php on line 8 |
| 29 | bool(true) | 29 | bool(true) |
| 30 | string(3) "300" | 30 | string(3) "300" |
| 31 | 31 | ||
| 32 | Warning: [snuffleupagus][0.0.0.0][ini_protection][log] INI value out of range in %a/ini_minmax.php on line 11 | 32 | Warning: [snuffleupagus][0.0.0.0][ini_protection][log] INI value 301 for `max_execution_time` out of range in %a/ini_minmax.php on line 11 |
| 33 | bool(true) | 33 | bool(true) |
| 34 | string(3) "300"%A | 34 | string(3) "300"%A |
diff --git a/src/tests/ini/ini_null.phpt b/src/tests/ini/ini_null.phpt index dfc2555..0835222 100644 --- a/src/tests/ini/ini_null.phpt +++ b/src/tests/ini/ini_null.phpt | |||
| @@ -21,6 +21,6 @@ string(15) "foo@example.com" | |||
| 21 | bool(false) | 21 | bool(false) |
| 22 | string(0) "" | 22 | string(0) "" |
| 23 | 23 | ||
| 24 | Warning: [snuffleupagus][0.0.0.0][ini_protection][log] new INI value must not be NULL or empty in %a/ini_null.php on line 8 | 24 | Warning: [snuffleupagus][0.0.0.0][ini_protection][log] new INI value for `unserialize_callback_func` must not be NULL or empty in %a/ini_null.php on line 8 |
| 25 | bool(true) | 25 | bool(true) |
| 26 | string(3) "def"%A | 26 | string(3) "def"%A |
diff --git a/src/tests/ini/ini_regexp.phpt b/src/tests/ini/ini_regexp.phpt index c7cab35..3d2156c 100644 --- a/src/tests/ini/ini_regexp.phpt +++ b/src/tests/ini/ini_regexp.phpt | |||
| @@ -15,5 +15,5 @@ var_dump(ini_get("highlight.comment")); | |||
| 15 | --EXPECTF-- | 15 | --EXPECTF-- |
| 16 | string(7) "#000aBc" | 16 | string(7) "#000aBc" |
| 17 | 17 | ||
| 18 | Warning: [snuffleupagus][0.0.0.0][ini_protection][log] INI value does not match regex in %a/ini_regexp.php on line 5 | 18 | Warning: [snuffleupagus][0.0.0.0][ini_protection][log] INI value `xxx` for `highlight.comment` does not match regex in %a/ini_regexp.php on line 5 |
| 19 | string(7) "#000aBc"%A | 19 | string(7) "#000aBc"%A |
diff --git a/src/tests/ini/ini_regexp_drop.phpt b/src/tests/ini/ini_regexp_drop.phpt index 432be8d..134e5c3 100644 --- a/src/tests/ini/ini_regexp_drop.phpt +++ b/src/tests/ini/ini_regexp_drop.phpt | |||
| @@ -10,4 +10,4 @@ var_dump(ini_set("user_agent", "Foo") === false); | |||
| 10 | var_dump(ini_get("user_agent")); | 10 | var_dump(ini_get("user_agent")); |
| 11 | ?> | 11 | ?> |
| 12 | --EXPECTF-- | 12 | --EXPECTF-- |
| 13 | Fatal error: [snuffleupagus][0.0.0.0][ini_protection][drop] INI value does not match regex in %a/ini_regexp_drop.php on line 2%A%A%A%A | 13 | Fatal error: [snuffleupagus][0.0.0.0][ini_protection][drop] INI value `Foo` for `user_agent` does not match regex in %a/ini_regexp_drop.php on line 2%A%A%A%A |
diff --git a/src/tests/ini/ini_regexp_drop_base64.phpt b/src/tests/ini/ini_regexp_drop_base64.phpt new file mode 100644 index 0000000..32076d5 --- /dev/null +++ b/src/tests/ini/ini_regexp_drop_base64.phpt | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | --TEST-- | ||
| 2 | INI protection .min() + .drop(), log base64 | ||
| 3 | --SKIPIF-- | ||
| 4 | <?php if (!extension_loaded("snuffleupagus")) print("skip"); ?> | ||
| 5 | --INI-- | ||
| 6 | sp.configuration_file={PWD}/config/sp.ini | ||
| 7 | --FILE-- | ||
| 8 | <?php | ||
| 9 | var_dump(ini_set("user_agent", "Foo\n\r") === false); | ||
| 10 | var_dump(ini_get("user_agent")); | ||
| 11 | ?> | ||
| 12 | --EXPECTF-- | ||
| 13 | Fatal error: [snuffleupagus][0.0.0.0][ini_protection][drop] INI value `Rm9vCg0=`(base64) for `user_agent` does not match regex in %a/ini_regexp_drop_base64.php on line 2%A%A%A%A | ||
