diff options
| author | Christian Göttsche | 2023-12-13 20:37:42 +0100 |
|---|---|---|
| committer | Julien Voisin | 2023-12-13 22:32:49 +0100 |
| commit | 3c720bec3a78775f37839256cfc4b2fea1348550 (patch) | |
| tree | 5231fd396c26896cf4b7eb5657fe504c0444b1bb | |
| parent | fed8c8f180b275147940b1c6bf8f2c95dfb1ada2 (diff) | |
print key and value on INI violations
On violations of INI settings include the key and if appropriate the
value in the log message. This helps to locate offenders and fine tune
the configuration itself.
| -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 | ||
