diff options
| author | Ben Fuhrmannek | 2022-04-19 19:01:52 +0200 |
|---|---|---|
| committer | Ben Fuhrmannek | 2022-04-19 19:01:52 +0200 |
| commit | 490dc4e2a43a5f8256f0b00d5efd3c2a84487905 (patch) | |
| tree | 0be55ca90c58151d4a36a50c5ecbd8c69be39dc7 /src/sp_execute.c | |
| parent | bb59f4396a7dd51a2490a12fd2714c68e19f7b66 (diff) | |
extended checks for readonly_exec, enabled by default
introduced config options:
readonly_exec.extended_checks() or xchecks()
readonly_exec.no_extended_checks() or noxchecks()
Diffstat (limited to '')
| -rw-r--r-- | src/sp_execute.c | 77 |
1 files changed, 64 insertions, 13 deletions
diff --git a/src/sp_execute.c b/src/sp_execute.c index 9cf44e1..3a474a3 100644 --- a/src/sp_execute.c +++ b/src/sp_execute.c | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | #include "php_snuffleupagus.h" | 1 | #include "php_snuffleupagus.h" |
| 2 | #include "ext/standard/php_string.h" | ||
| 2 | 3 | ||
| 3 | static void (*orig_execute_ex)(zend_execute_data *execute_data) = NULL; | 4 | static void (*orig_execute_ex)(zend_execute_data *execute_data) = NULL; |
| 4 | static void (*orig_zend_execute_internal)(zend_execute_data *execute_data, | 5 | static void (*orig_zend_execute_internal)(zend_execute_data *execute_data, |
| @@ -12,22 +13,72 @@ static zend_result (*orig_zend_stream_open)(zend_file_handle *handle) = NULL; | |||
| 12 | // FIXME handle symlink | 13 | // FIXME handle symlink |
| 13 | ZEND_COLD static inline void terminate_if_writable(const char *filename) { | 14 | ZEND_COLD static inline void terminate_if_writable(const char *filename) { |
| 14 | const sp_config_readonly_exec *config_ro_exec = &(SPCFG(readonly_exec)); | 15 | const sp_config_readonly_exec *config_ro_exec = &(SPCFG(readonly_exec)); |
| 16 | char *errmsg = "unknown access problem"; | ||
| 17 | |||
| 18 | // check write access | ||
| 15 | if (0 == access(filename, W_OK)) { | 19 | if (0 == access(filename, W_OK)) { |
| 16 | if (config_ro_exec->dump) { | 20 | errmsg = "Attempted execution of a writable file"; |
| 17 | sp_log_request(config_ro_exec->dump, config_ro_exec->textual_representation); | 21 | goto violation; |
| 18 | } | 22 | } |
| 19 | if (true == config_ro_exec->simulation) { | 23 | if (errno != EACCES) { |
| 20 | sp_log_simulation("readonly_exec", "Attempted execution of a writable file (%s)", filename); | 24 | goto err; |
| 21 | } else { | 25 | } |
| 22 | sp_log_drop("readonly_exec", "Attempted execution of a writable file (%s)", filename); | 26 | |
| 23 | } | 27 | // other checks are 'extended checks' that can be enabled/disabled via config |
| 28 | if (!config_ro_exec->extended_checks) { | ||
| 29 | return; | ||
| 30 | } | ||
| 31 | |||
| 32 | // check effective uid | ||
| 33 | struct stat buf; | ||
| 34 | if (0 != stat(filename, &buf)) { | ||
| 35 | goto err; | ||
| 36 | } | ||
| 37 | if (buf.st_uid == geteuid()) { | ||
| 38 | errmsg = "Attempted execution of file owned by process"; | ||
| 39 | goto violation; | ||
| 40 | } | ||
| 41 | |||
| 42 | // check write access on directory | ||
| 43 | char *dirname = estrndup(filename, strlen(filename)); | ||
| 44 | php_dirname(dirname, strlen(dirname)); | ||
| 45 | if (0 == access(dirname, W_OK)) { | ||
| 46 | errmsg = "Attempted execution of file in writable directory"; | ||
| 47 | efree(dirname); | ||
| 48 | goto violation; | ||
| 49 | } | ||
| 50 | if (errno != EACCES) { | ||
| 51 | efree(dirname); | ||
| 52 | goto err; | ||
| 53 | } | ||
| 54 | |||
| 55 | // check effecite uid of directory | ||
| 56 | if (0 != stat(dirname, &buf)) { | ||
| 57 | efree(dirname); | ||
| 58 | goto err; | ||
| 59 | } | ||
| 60 | efree(dirname); | ||
| 61 | if (buf.st_uid == geteuid()) { | ||
| 62 | errmsg = "Attempted execution of file in directory owned by process"; | ||
| 63 | goto violation; | ||
| 64 | } | ||
| 65 | |||
| 66 | // we would actually need to check all parent directories as well, but that task is left for other tools | ||
| 67 | return; | ||
| 68 | |||
| 69 | violation: | ||
| 70 | if (config_ro_exec->dump) { | ||
| 71 | sp_log_request(config_ro_exec->dump, config_ro_exec->textual_representation); | ||
| 72 | } | ||
| 73 | if (config_ro_exec->simulation) { | ||
| 74 | sp_log_simulation("readonly_exec", "%s (%s)", errmsg, filename); | ||
| 24 | } else { | 75 | } else { |
| 25 | if (EACCES != errno) { | 76 | sp_log_drop("readonly_exec", "%s (%s)", errmsg, filename); |
| 26 | // LCOV_EXCL_START | ||
| 27 | sp_log_err("Writable execution", "Error while accessing %s: %s", filename, strerror(errno)); | ||
| 28 | // LCOV_EXCL_STOP | ||
| 29 | } | ||
| 30 | } | 77 | } |
| 78 | return; | ||
| 79 | |||
| 80 | err: | ||
| 81 | sp_log_err("readonly_exec", "Error while accessing %s: %s", filename, strerror(errno)); | ||
| 31 | } | 82 | } |
| 32 | 83 | ||
| 33 | inline static void is_builtin_matching( | 84 | inline static void is_builtin_matching( |
