summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/snuffleupagus.c2
-rw-r--r--src/sp_config.h1
-rw-r--r--src/sp_config_keywords.c7
-rw-r--r--src/sp_execute.c77
4 files changed, 73 insertions, 14 deletions
diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c
index ebb7f9c..c723199 100644
--- a/src/snuffleupagus.c
+++ b/src/snuffleupagus.c
@@ -348,6 +348,7 @@ static void dump_config() {
348 add_assoc_bool(&arr, "readonly_exec.enable", SPCFG(readonly_exec).enable); 348 add_assoc_bool(&arr, "readonly_exec.enable", SPCFG(readonly_exec).enable);
349 add_assoc_bool(&arr, "readonly_exec.sim", SPCFG(readonly_exec).simulation); 349 add_assoc_bool(&arr, "readonly_exec.sim", SPCFG(readonly_exec).simulation);
350 ADD_ASSOC_ZSTR(&arr, SP_TOKEN_READONLY_EXEC "." SP_TOKEN_DUMP, SPCFG(readonly_exec).dump); 350 ADD_ASSOC_ZSTR(&arr, SP_TOKEN_READONLY_EXEC "." SP_TOKEN_DUMP, SPCFG(readonly_exec).dump);
351 add_assoc_bool(&arr, "readonly_exec.extended_checks", SPCFG(readonly_exec).extended_checks);
351 352
352 add_assoc_bool(&arr, "global_strict.enable", SPCFG(global_strict).enable); 353 add_assoc_bool(&arr, "global_strict.enable", SPCFG(global_strict).enable);
353 354
@@ -499,6 +500,7 @@ static PHP_INI_MH(OnUpdateConfiguration) {
499 500
500 // set some defaults 501 // set some defaults
501 SPCFG(show_old_php_warning) = true; 502 SPCFG(show_old_php_warning) = true;
503 SPCFG(readonly_exec).extended_checks = true;
502 504
503 char *str = new_value->val; 505 char *str = new_value->val;
504 506
diff --git a/src/sp_config.h b/src/sp_config.h
index 6d48240..e7d4e4d 100644
--- a/src/sp_config.h
+++ b/src/sp_config.h
@@ -35,6 +35,7 @@ typedef struct {
35typedef struct { 35typedef struct {
36 bool enable; 36 bool enable;
37 bool simulation; 37 bool simulation;
38 bool extended_checks;
38 zend_string *dump; 39 zend_string *dump;
39 zend_string *textual_representation; 40 zend_string *textual_representation;
40} sp_config_readonly_exec; 41} sp_config_readonly_exec;
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c
index f7be731..e0e5166 100644
--- a/src/sp_config_keywords.c
+++ b/src/sp_config_keywords.c
@@ -96,7 +96,7 @@ SP_PARSE_FN(parse_unserialize) {
96} 96}
97 97
98SP_PARSE_FN(parse_readonly_exec) { 98SP_PARSE_FN(parse_readonly_exec) {
99 bool enable = false, disable = false; 99 bool enable = false, disable = false, xchecks = false, no_xchecks = false;
100 sp_config_readonly_exec *cfg = (sp_config_readonly_exec*)retval; 100 sp_config_readonly_exec *cfg = (sp_config_readonly_exec*)retval;
101 101
102 sp_config_keyword config_keywords[] = { 102 sp_config_keyword config_keywords[] = {
@@ -105,6 +105,10 @@ SP_PARSE_FN(parse_readonly_exec) {
105 {parse_empty, SP_TOKEN_SIMULATION, &(cfg->simulation)}, 105 {parse_empty, SP_TOKEN_SIMULATION, &(cfg->simulation)},
106 {parse_empty, SP_TOKEN_SIM, &(cfg->simulation)}, 106 {parse_empty, SP_TOKEN_SIM, &(cfg->simulation)},
107 {parse_str, SP_TOKEN_DUMP, &(cfg->dump)}, 107 {parse_str, SP_TOKEN_DUMP, &(cfg->dump)},
108 {parse_empty, "extended_checks", &(xchecks)},
109 {parse_empty, "xchecks", &(xchecks)},
110 {parse_empty, "no_extended_checks", &(no_xchecks)},
111 {parse_empty, "noxchecks", &(no_xchecks)},
108 {0, 0, 0}}; 112 {0, 0, 0}};
109 113
110 SP_PROCESS_CONFIG_KEYWORDS_ERR(); 114 SP_PROCESS_CONFIG_KEYWORDS_ERR();
@@ -112,6 +116,7 @@ SP_PARSE_FN(parse_readonly_exec) {
112 cfg->textual_representation = sp_get_textual_representation(parsed_rule); 116 cfg->textual_representation = sp_get_textual_representation(parsed_rule);
113 117
114 SP_SET_ENABLE_DISABLE(enable, disable, cfg->enable); 118 SP_SET_ENABLE_DISABLE(enable, disable, cfg->enable);
119 if (xchecks) { cfg->extended_checks = true; } else if (no_xchecks) { cfg->extended_checks = false; }
115 120
116 return SP_PARSER_STOP; 121 return SP_PARSER_STOP;
117} 122}
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
3static void (*orig_execute_ex)(zend_execute_data *execute_data) = NULL; 4static void (*orig_execute_ex)(zend_execute_data *execute_data) = NULL;
4static void (*orig_zend_execute_internal)(zend_execute_data *execute_data, 5static 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
13ZEND_COLD static inline void terminate_if_writable(const char *filename) { 14ZEND_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
69violation:
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
80err:
81 sp_log_err("readonly_exec", "Error while accessing %s: %s", filename, strerror(errno));
31} 82}
32 83
33inline static void is_builtin_matching( 84inline static void is_builtin_matching(