From 868f96c759b6650d88ff9f4fbc5c048302134248 Mon Sep 17 00:00:00 2001 From: Sebastien Blot Date: Wed, 20 Sep 2017 10:11:01 +0200 Subject: [PATCH] Initial import --- .gitignore | 39 + LICENSE | 165 ++++ Makefile | 32 + README.md | 15 + config/default.ini | 54 ++ config/examples.ini | 47 + debian/changelog | 5 + debian/compat | 1 + debian/control | 14 + debian/copyright | 8 + debian/docs | 1 + debian/rules | 27 + debian/watch | 2 + doc/Makefile | 20 + doc/source/_static/custom.css | 4 + doc/source/_static/sp.jpg | Bin 0 -> 36559 bytes doc/source/conf.py | 178 ++++ doc/source/config.rst | 283 ++++++ doc/source/download.rst | 16 + doc/source/faq.rst | 196 ++++ doc/source/features.rst | 352 ++++++++ doc/source/index.rst | 30 + doc/source/installation.rst | 33 + doc/source/papers.rst | 16 + scripts/generate_rules.php | 43 + src/bench/bench.php | 422 +++++++++ src/bench/micro_bench.php | 358 ++++++++ src/config.m4 | 35 + src/config.w32 | 13 + src/php_snuffleupagus.h | 71 ++ src/snuffleupagus.c | 222 +++++ src/snuffleupagus.php | 21 + src/sp_compile.c | 53 ++ src/sp_compile.h | 6 + src/sp_config.c | 193 ++++ src/sp_config.h | 206 +++++ src/sp_config_keywords.c | 268 ++++++ src/sp_config_keywords.h | 16 + src/sp_config_utils.c | 211 +++++ src/sp_config_utils.h | 8 + src/sp_cookie_encryption.c | 216 +++++ src/sp_cookie_encryption.h | 17 + src/sp_disable_xxe.c | 25 + src/sp_disable_xxe.h | 6 + src/sp_disabled_functions.c | 356 ++++++++ src/sp_disabled_functions.h | 11 + src/sp_execute.c | 100 +++ src/sp_execute.h | 6 + src/sp_harden_rand.c | 77 ++ src/sp_harden_rand.h | 10 + src/sp_list.c | 37 + src/sp_list.h | 15 + src/sp_network_utils.c | 159 ++++ src/sp_network_utils.h | 8 + src/sp_unserialize.c | 111 +++ src/sp_unserialize.h | 9 + src/sp_upload_validation.c | 92 ++ src/sp_upload_validation.h | 9 + src/sp_utils.c | 425 +++++++++ src/sp_utils.h | 68 ++ src/tests/broken_conf.phpt | 10 + src/tests/broken_conf2.phpt | 9 + src/tests/broken_conf_config_regexp.phpt | 10 + src/tests/broken_conf_enable_disable.phpt | 9 + src/tests/broken_conf_expecting_bool.phpt | 9 + src/tests/broken_conf_expecting_int.phpt | 9 + src/tests/broken_conf_invalid_cidr.phpt | 9 + src/tests/broken_conf_invalid_cidr6.phpt | 9 + .../broken_conf_invalid_cidr6_no_slash.phpt | 9 + .../broken_conf_invalid_cidr6_too_big.phpt | 9 + src/tests/broken_conf_invalid_cidr_value.phpt | 11 + src/tests/broken_conf_invalid_type.phpt | 9 + src/tests/broken_conf_line_empty_string.phpt | 9 + src/tests/broken_conf_line_no_closing.phpt | 9 + src/tests/broken_conf_line_too_long.phpt | 10 + src/tests/broken_conf_lots_of_quotes.phpt | 9 + src/tests/broken_conf_mutually_exclusive.phpt | 9 + .../broken_conf_mutually_exclusive2.phpt | 9 + .../broken_conf_mutually_exclusive3.phpt | 9 + .../broken_conf_mutually_exclusive4.phpt | 9 + .../broken_conf_mutually_exclusive5.phpt | 9 + .../broken_conf_mutually_exclusive6.phpt | 9 + .../broken_conf_mutually_exclusive7.phpt | 9 + .../broken_conf_mutually_exclusive8.phpt | 9 + src/tests/broken_conf_no_closing_misc.phpt | 10 + src/tests/broken_conf_weird_keyword.phpt | 9 + src/tests/broken_conf_wrong_quotes.phpt | 9 + src/tests/broken_conf_wrong_type.phpt | 9 + src/tests/broken_regexp.phpt | 9 + .../config/borken_conf_enable_disable.ini | 1 + src/tests/config/broken_conf.ini | 1 + src/tests/config/broken_conf2.ini | 1 + .../config/broken_conf_expecting_bool.ini | 5 + .../config/broken_conf_expecting_int.ini | 2 + src/tests/config/broken_conf_invalid_cidr.ini | 1 + .../config/broken_conf_invalid_cidr6.ini | 1 + .../broken_conf_invalid_cidr6_no_slash.ini | 1 + .../broken_conf_invalid_cidr6_too_big.ini | 1 + .../config/broken_conf_invalid_cidr_value.ini | 1 + src/tests/config/broken_conf_invalid_type.ini | 1 + .../config/broken_conf_line_empty_string.ini | 1 + .../config/broken_conf_line_no_closing.ini | 1 + .../config/broken_conf_line_too_long.ini | 1 + .../config/broken_conf_lots_of_quotes.ini | 1 + .../config/broken_conf_mutually_exclusive.ini | 1 + .../broken_conf_mutually_exclusive2.ini | 1 + .../broken_conf_mutually_exclusive3.ini | 1 + .../broken_conf_mutually_exclusive4.ini | 1 + .../broken_conf_mutually_exclusive5.ini | 1 + .../broken_conf_mutually_exclusive6.ini | 1 + .../broken_conf_mutually_exclusive7.ini | 1 + .../broken_conf_mutually_exclusive8.ini | 1 + .../config/broken_conf_no_closing_misc.ini | 1 + src/tests/config/broken_conf_to_few_args.ini | 1 + .../config/broken_conf_weird_keyword.ini | 1 + src/tests/config/broken_conf_wrong_quotes.ini | 1 + src/tests/config/broken_conf_wrong_type.ini | 5 + src/tests/config/broken_config_regexp.ini | 1 + src/tests/config/broken_regexp.ini | 1 + src/tests/config/config_disable_writable.ini | 1 + .../config_disable_writable_disabled.ini | 1 + .../config_disable_writable_simulation.ini | 1 + .../config_disabled_functions_filename_r.ini | 2 + .../config_disabled_functions_method.ini | 3 + .../config_disabled_functions_name_r.ini | 2 + .../config_disabled_functions_name_type.ini | 1 + .../config_disabled_functions_namespace.ini | 2 + .../config_disabled_functions_nul_byte.ini | 1 + .../config_disabled_functions_param.ini | 6 + .../config_disabled_functions_param_alias.ini | 2 + .../config_disabled_functions_param_allow.ini | 3 + .../config_disabled_functions_param_array.ini | 4 + .../config_disabled_functions_param_int.ini | 2 + .../config_disabled_functions_param_r.ini | 1 + ...onfig_disabled_functions_param_runtime.ini | 1 + ...led_functions_param_str_representation.ini | 1 + .../config_disabled_functions_require.ini | 1 + .../config_disabled_functions_ret_allow.ini | 2 + ...fig_disabled_functions_ret_allow_value.ini | 1 + ...nfig_disabled_functions_ret_right_hash.ini | 4 + ...nfig_disabled_functions_ret_simulation.ini | 3 + .../config_disabled_functions_right_hash.ini | 3 + .../config/config_disabled_user_functions.ini | 1 + src/tests/config/config_encrypted_cookies.ini | 3 + .../config_noncore_function_hooking.ini | 1 + .../config/config_rand_harden_disabled.ini | 1 + src/tests/config/config_serialize.ini | 2 + src/tests/config/config_serialize_sim.ini | 2 + src/tests/config/disable_xxe.ini | 1 + src/tests/config/disable_xxe_disable.ini | 1 + .../config/disabled_function_local_var.ini | 2 + .../disabled_function_super_global_var.ini | 1 + src/tests/config/disabled_functions.ini | 7 + src/tests/config/disabled_functions_cidr.ini | 4 + src/tests/config/disabled_functions_mb.ini | 2 + src/tests/config/disabled_functions_ret.ini | 5 + .../config/disabled_functions_ret_type.ini | 1 + .../disabled_functions_ret_type_double.ini | 1 + .../disabled_functions_ret_type_long.ini | 1 + .../disabled_functions_ret_type_resource.ini | 1 + .../disabled_functions_ret_type_str.ini | 1 + .../disabled_functions_ret_type_true.ini | 1 + .../config/disabled_functions_retval.ini | 1 + .../config/disabled_functions_retval_rx.ini | 1 + .../config/disabled_functions_zero_cidr.ini | 1 + src/tests/config/dump_request.ini | 1 + .../config/dump_request_invalid_folder.ini | 1 + src/tests/config/empty.ini | 0 src/tests/config/empty_conf.ini | 0 src/tests/config/encryption_key_only.ini | 1 + src/tests/config/global_strict.ini | 1 + src/tests/config/global_strict_disabled.ini | 1 + src/tests/config/harden_rand.ini | 1 + src/tests/config/upload_validation.ini | 2 + .../config/upload_validation_invalid.ini | 1 + src/tests/config/upload_validation_ko.ini | 1 + .../upload_validation_ko_simulation.ini | 1 + .../config/upload_validation_no_exist.ini | 1 + .../config/upload_validation_non_exec.ini | 1 + src/tests/config/upload_validation_ok.ini | 1 + src/tests/data/upload_invalid.sh | 1 + src/tests/data/upload_ko.sh | 2 + src/tests/data/upload_no_exec.sh | 2 + src/tests/data/upload_ok.sh | 2 + src/tests/deny_writable_execution.phpt | 44 + .../deny_writable_execution_disabled.phpt | 32 + .../deny_writable_execution_simulation.phpt | 45 + src/tests/disable_xxe_dom.phpt | 71 ++ src/tests/disable_xxe_dom_disabled.phpt | 56 ++ src/tests/disable_xxe_simplexml.phpt | 52 ++ src/tests/disable_xxe_simplexml_oop.phpt | 52 ++ src/tests/disable_xxe_xml_parse.phpt | 104 +++ src/tests/disabled_function_local_var.phpt | 24 + .../disabled_function_super_global_var.phpt | 20 + src/tests/disabled_functions.phpt | 21 + src/tests/disabled_functions_cidr.phpt | 18 + src/tests/disabled_functions_cidr_6.phpt | 18 + src/tests/disabled_functions_filename_r.phpt | 14 + src/tests/disabled_functions_mb.phpt | 12 + src/tests/disabled_functions_method.phpt | 29 + src/tests/disabled_functions_name_r.phpt | 15 + src/tests/disabled_functions_name_type.phpt | 14 + src/tests/disabled_functions_namespace.phpt | 31 + src/tests/disabled_functions_noconf.phpt | 12 + src/tests/disabled_functions_nul_byte.phpt | 15 + src/tests/disabled_functions_param.phpt | 24 + src/tests/disabled_functions_param_alias.phpt | 14 + src/tests/disabled_functions_param_allow.phpt | 14 + src/tests/disabled_functions_param_array.phpt | 37 + src/tests/disabled_functions_param_int.phpt | 25 + src/tests/disabled_functions_param_r.phpt | 14 + ...ed_functions_param_str_representation.phpt | 25 + src/tests/disabled_functions_parse_class.phpt | 22 + src/tests/disabled_functions_require.phpt | 25 + src/tests/disabled_functions_ret.phpt | 13 + src/tests/disabled_functions_ret2.phpt | 12 + src/tests/disabled_functions_ret3.phpt | 22 + src/tests/disabled_functions_ret_allow.phpt | 13 + .../disabled_functions_ret_allow_value.phpt | 12 + .../disabled_functions_ret_right_hash.phpt | 12 + .../disabled_functions_ret_simulation.phpt | 18 + src/tests/disabled_functions_ret_type.phpt | 16 + .../disabled_functions_ret_type_double.phpt | 12 + .../disabled_functions_ret_type_long.phpt | 12 + .../disabled_functions_ret_type_resource.phpt | 12 + .../disabled_functions_ret_type_str.phpt | 12 + .../disabled_functions_ret_type_true.phpt | 16 + src/tests/disabled_functions_ret_val.phpt | 14 + src/tests/disabled_functions_ret_val_rx.phpt | 14 + src/tests/disabled_functions_right_hash.phpt | 12 + src/tests/disabled_functions_runtime.phpt | 31 + src/tests/disabled_functions_zero_cidr.phpt | 18 + src/tests/disabled_option.phpt | 16 + src/tests/disabled_user_functions.phpt | 15 + src/tests/dump_request.phpt | 39 + src/tests/dump_request_invalid_folder.phpt | 25 + src/tests/dump_request_too_big.phpt | 42 + src/tests/empty_conf.phpt | 8 + src/tests/encrypt_cookies.phpt | 22 + src/tests/encrypt_cookies2.phpt | 23 + src/tests/encrypt_cookies3.phpt | 23 + src/tests/encrypt_cookies4.phpt | 23 + .../encrypt_cookies_invalid_decryption.phpt | 23 + .../encrypt_cookies_invalid_decryption2.phpt | 23 + .../encrypt_cookies_invalid_decryption3.phpt | 21 + src/tests/encryption_key_only.phpt | 13 + src/tests/example_configuration.phpt | 12 + src/tests/global_strict.phpt | 16 + src/tests/global_strict_disabled.phpt | 14 + src/tests/harden_mt_rand.phpt | 22 + src/tests/harden_rand.phpt | 24 + src/tests/harden_rand_noargs.phpt | 62 ++ src/tests/inexistent_conf_file.phpt | 10 + src/tests/loading.phpt | 10 + src/tests/noncore_function_hooking.phpt | 15 + src/tests/phpinfo_presence.phpt | 19 + src/tests/serialize.phpt | 13 + src/tests/setcookie.phpt | 35 + src/tests/shipped_configuration.phpt | 12 + src/tests/unserialize.phpt | 13 + src/tests/unserialize_fail.phpt | 23 + src/tests/unserialize_sim.phpt | 17 + src/tests/upload_validation.phpt | 16 + src/tests/upload_validation_invalid.phpt | 17 + src/tests/upload_validation_ko.phpt | 14 + src/tests/upload_validation_no_exec.phpt | 32 + src/tests/upload_validation_nocrash.phpt | 12 + src/tests/upload_validation_ok.phpt | 17 + src/tweetnacl.c | 842 ++++++++++++++++++ src/tweetnacl.h | 277 ++++++ 270 files changed, 8888 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 config/default.ini create mode 100644 config/examples.ini create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100755 debian/rules create mode 100644 debian/watch create mode 100644 doc/Makefile create mode 100644 doc/source/_static/custom.css create mode 100644 doc/source/_static/sp.jpg create mode 100644 doc/source/conf.py create mode 100644 doc/source/config.rst create mode 100644 doc/source/download.rst create mode 100644 doc/source/faq.rst create mode 100644 doc/source/features.rst create mode 100644 doc/source/index.rst create mode 100644 doc/source/installation.rst create mode 100644 doc/source/papers.rst create mode 100644 scripts/generate_rules.php create mode 100644 src/bench/bench.php create mode 100644 src/bench/micro_bench.php create mode 100644 src/config.m4 create mode 100644 src/config.w32 create mode 100644 src/php_snuffleupagus.h create mode 100644 src/snuffleupagus.c create mode 100644 src/snuffleupagus.php create mode 100644 src/sp_compile.c create mode 100644 src/sp_compile.h create mode 100644 src/sp_config.c create mode 100644 src/sp_config.h create mode 100644 src/sp_config_keywords.c create mode 100644 src/sp_config_keywords.h create mode 100644 src/sp_config_utils.c create mode 100644 src/sp_config_utils.h create mode 100644 src/sp_cookie_encryption.c create mode 100644 src/sp_cookie_encryption.h create mode 100644 src/sp_disable_xxe.c create mode 100644 src/sp_disable_xxe.h create mode 100644 src/sp_disabled_functions.c create mode 100644 src/sp_disabled_functions.h create mode 100644 src/sp_execute.c create mode 100644 src/sp_execute.h create mode 100644 src/sp_harden_rand.c create mode 100644 src/sp_harden_rand.h create mode 100644 src/sp_list.c create mode 100644 src/sp_list.h create mode 100644 src/sp_network_utils.c create mode 100644 src/sp_network_utils.h create mode 100644 src/sp_unserialize.c create mode 100644 src/sp_unserialize.h create mode 100644 src/sp_upload_validation.c create mode 100644 src/sp_upload_validation.h create mode 100644 src/sp_utils.c create mode 100644 src/sp_utils.h create mode 100644 src/tests/broken_conf.phpt create mode 100644 src/tests/broken_conf2.phpt create mode 100644 src/tests/broken_conf_config_regexp.phpt create mode 100644 src/tests/broken_conf_enable_disable.phpt create mode 100644 src/tests/broken_conf_expecting_bool.phpt create mode 100644 src/tests/broken_conf_expecting_int.phpt create mode 100644 src/tests/broken_conf_invalid_cidr.phpt create mode 100644 src/tests/broken_conf_invalid_cidr6.phpt create mode 100644 src/tests/broken_conf_invalid_cidr6_no_slash.phpt create mode 100644 src/tests/broken_conf_invalid_cidr6_too_big.phpt create mode 100644 src/tests/broken_conf_invalid_cidr_value.phpt create mode 100644 src/tests/broken_conf_invalid_type.phpt create mode 100644 src/tests/broken_conf_line_empty_string.phpt create mode 100644 src/tests/broken_conf_line_no_closing.phpt create mode 100644 src/tests/broken_conf_line_too_long.phpt create mode 100644 src/tests/broken_conf_lots_of_quotes.phpt create mode 100644 src/tests/broken_conf_mutually_exclusive.phpt create mode 100644 src/tests/broken_conf_mutually_exclusive2.phpt create mode 100644 src/tests/broken_conf_mutually_exclusive3.phpt create mode 100644 src/tests/broken_conf_mutually_exclusive4.phpt create mode 100644 src/tests/broken_conf_mutually_exclusive5.phpt create mode 100644 src/tests/broken_conf_mutually_exclusive6.phpt create mode 100644 src/tests/broken_conf_mutually_exclusive7.phpt create mode 100644 src/tests/broken_conf_mutually_exclusive8.phpt create mode 100644 src/tests/broken_conf_no_closing_misc.phpt create mode 100644 src/tests/broken_conf_weird_keyword.phpt create mode 100644 src/tests/broken_conf_wrong_quotes.phpt create mode 100644 src/tests/broken_conf_wrong_type.phpt create mode 100644 src/tests/broken_regexp.phpt create mode 100644 src/tests/config/borken_conf_enable_disable.ini create mode 100644 src/tests/config/broken_conf.ini create mode 100644 src/tests/config/broken_conf2.ini create mode 100644 src/tests/config/broken_conf_expecting_bool.ini create mode 100644 src/tests/config/broken_conf_expecting_int.ini create mode 100644 src/tests/config/broken_conf_invalid_cidr.ini create mode 100644 src/tests/config/broken_conf_invalid_cidr6.ini create mode 100644 src/tests/config/broken_conf_invalid_cidr6_no_slash.ini create mode 100644 src/tests/config/broken_conf_invalid_cidr6_too_big.ini create mode 100644 src/tests/config/broken_conf_invalid_cidr_value.ini create mode 100644 src/tests/config/broken_conf_invalid_type.ini create mode 100644 src/tests/config/broken_conf_line_empty_string.ini create mode 100644 src/tests/config/broken_conf_line_no_closing.ini create mode 100644 src/tests/config/broken_conf_line_too_long.ini create mode 100644 src/tests/config/broken_conf_lots_of_quotes.ini create mode 100644 src/tests/config/broken_conf_mutually_exclusive.ini create mode 100644 src/tests/config/broken_conf_mutually_exclusive2.ini create mode 100644 src/tests/config/broken_conf_mutually_exclusive3.ini create mode 100644 src/tests/config/broken_conf_mutually_exclusive4.ini create mode 100644 src/tests/config/broken_conf_mutually_exclusive5.ini create mode 100644 src/tests/config/broken_conf_mutually_exclusive6.ini create mode 100644 src/tests/config/broken_conf_mutually_exclusive7.ini create mode 100644 src/tests/config/broken_conf_mutually_exclusive8.ini create mode 100644 src/tests/config/broken_conf_no_closing_misc.ini create mode 100644 src/tests/config/broken_conf_to_few_args.ini create mode 100644 src/tests/config/broken_conf_weird_keyword.ini create mode 100644 src/tests/config/broken_conf_wrong_quotes.ini create mode 100644 src/tests/config/broken_conf_wrong_type.ini create mode 100644 src/tests/config/broken_config_regexp.ini create mode 100644 src/tests/config/broken_regexp.ini create mode 100644 src/tests/config/config_disable_writable.ini create mode 100644 src/tests/config/config_disable_writable_disabled.ini create mode 100644 src/tests/config/config_disable_writable_simulation.ini create mode 100644 src/tests/config/config_disabled_functions_filename_r.ini create mode 100644 src/tests/config/config_disabled_functions_method.ini create mode 100644 src/tests/config/config_disabled_functions_name_r.ini create mode 100644 src/tests/config/config_disabled_functions_name_type.ini create mode 100644 src/tests/config/config_disabled_functions_namespace.ini create mode 100644 src/tests/config/config_disabled_functions_nul_byte.ini create mode 100644 src/tests/config/config_disabled_functions_param.ini create mode 100644 src/tests/config/config_disabled_functions_param_alias.ini create mode 100644 src/tests/config/config_disabled_functions_param_allow.ini create mode 100644 src/tests/config/config_disabled_functions_param_array.ini create mode 100644 src/tests/config/config_disabled_functions_param_int.ini create mode 100644 src/tests/config/config_disabled_functions_param_r.ini create mode 100644 src/tests/config/config_disabled_functions_param_runtime.ini create mode 100644 src/tests/config/config_disabled_functions_param_str_representation.ini create mode 100644 src/tests/config/config_disabled_functions_require.ini create mode 100644 src/tests/config/config_disabled_functions_ret_allow.ini create mode 100644 src/tests/config/config_disabled_functions_ret_allow_value.ini create mode 100644 src/tests/config/config_disabled_functions_ret_right_hash.ini create mode 100644 src/tests/config/config_disabled_functions_ret_simulation.ini create mode 100644 src/tests/config/config_disabled_functions_right_hash.ini create mode 100644 src/tests/config/config_disabled_user_functions.ini create mode 100644 src/tests/config/config_encrypted_cookies.ini create mode 100644 src/tests/config/config_noncore_function_hooking.ini create mode 100644 src/tests/config/config_rand_harden_disabled.ini create mode 100644 src/tests/config/config_serialize.ini create mode 100644 src/tests/config/config_serialize_sim.ini create mode 100644 src/tests/config/disable_xxe.ini create mode 100644 src/tests/config/disable_xxe_disable.ini create mode 100644 src/tests/config/disabled_function_local_var.ini create mode 100644 src/tests/config/disabled_function_super_global_var.ini create mode 100644 src/tests/config/disabled_functions.ini create mode 100644 src/tests/config/disabled_functions_cidr.ini create mode 100644 src/tests/config/disabled_functions_mb.ini create mode 100644 src/tests/config/disabled_functions_ret.ini create mode 100644 src/tests/config/disabled_functions_ret_type.ini create mode 100644 src/tests/config/disabled_functions_ret_type_double.ini create mode 100644 src/tests/config/disabled_functions_ret_type_long.ini create mode 100644 src/tests/config/disabled_functions_ret_type_resource.ini create mode 100644 src/tests/config/disabled_functions_ret_type_str.ini create mode 100644 src/tests/config/disabled_functions_ret_type_true.ini create mode 100644 src/tests/config/disabled_functions_retval.ini create mode 100644 src/tests/config/disabled_functions_retval_rx.ini create mode 100644 src/tests/config/disabled_functions_zero_cidr.ini create mode 100644 src/tests/config/dump_request.ini create mode 100644 src/tests/config/dump_request_invalid_folder.ini create mode 100644 src/tests/config/empty.ini create mode 100644 src/tests/config/empty_conf.ini create mode 100644 src/tests/config/encryption_key_only.ini create mode 100644 src/tests/config/global_strict.ini create mode 100644 src/tests/config/global_strict_disabled.ini create mode 100644 src/tests/config/harden_rand.ini create mode 100644 src/tests/config/upload_validation.ini create mode 100644 src/tests/config/upload_validation_invalid.ini create mode 100644 src/tests/config/upload_validation_ko.ini create mode 100644 src/tests/config/upload_validation_ko_simulation.ini create mode 100644 src/tests/config/upload_validation_no_exist.ini create mode 100644 src/tests/config/upload_validation_non_exec.ini create mode 100644 src/tests/config/upload_validation_ok.ini create mode 100755 src/tests/data/upload_invalid.sh create mode 100755 src/tests/data/upload_ko.sh create mode 100644 src/tests/data/upload_no_exec.sh create mode 100755 src/tests/data/upload_ok.sh create mode 100644 src/tests/deny_writable_execution.phpt create mode 100644 src/tests/deny_writable_execution_disabled.phpt create mode 100644 src/tests/deny_writable_execution_simulation.phpt create mode 100644 src/tests/disable_xxe_dom.phpt create mode 100644 src/tests/disable_xxe_dom_disabled.phpt create mode 100644 src/tests/disable_xxe_simplexml.phpt create mode 100644 src/tests/disable_xxe_simplexml_oop.phpt create mode 100644 src/tests/disable_xxe_xml_parse.phpt create mode 100644 src/tests/disabled_function_local_var.phpt create mode 100644 src/tests/disabled_function_super_global_var.phpt create mode 100644 src/tests/disabled_functions.phpt create mode 100644 src/tests/disabled_functions_cidr.phpt create mode 100644 src/tests/disabled_functions_cidr_6.phpt create mode 100644 src/tests/disabled_functions_filename_r.phpt create mode 100644 src/tests/disabled_functions_mb.phpt create mode 100644 src/tests/disabled_functions_method.phpt create mode 100644 src/tests/disabled_functions_name_r.phpt create mode 100644 src/tests/disabled_functions_name_type.phpt create mode 100644 src/tests/disabled_functions_namespace.phpt create mode 100644 src/tests/disabled_functions_noconf.phpt create mode 100644 src/tests/disabled_functions_nul_byte.phpt create mode 100644 src/tests/disabled_functions_param.phpt create mode 100644 src/tests/disabled_functions_param_alias.phpt create mode 100644 src/tests/disabled_functions_param_allow.phpt create mode 100644 src/tests/disabled_functions_param_array.phpt create mode 100644 src/tests/disabled_functions_param_int.phpt create mode 100644 src/tests/disabled_functions_param_r.phpt create mode 100644 src/tests/disabled_functions_param_str_representation.phpt create mode 100644 src/tests/disabled_functions_parse_class.phpt create mode 100644 src/tests/disabled_functions_require.phpt create mode 100644 src/tests/disabled_functions_ret.phpt create mode 100644 src/tests/disabled_functions_ret2.phpt create mode 100644 src/tests/disabled_functions_ret3.phpt create mode 100644 src/tests/disabled_functions_ret_allow.phpt create mode 100644 src/tests/disabled_functions_ret_allow_value.phpt create mode 100644 src/tests/disabled_functions_ret_right_hash.phpt create mode 100644 src/tests/disabled_functions_ret_simulation.phpt create mode 100644 src/tests/disabled_functions_ret_type.phpt create mode 100644 src/tests/disabled_functions_ret_type_double.phpt create mode 100644 src/tests/disabled_functions_ret_type_long.phpt create mode 100644 src/tests/disabled_functions_ret_type_resource.phpt create mode 100644 src/tests/disabled_functions_ret_type_str.phpt create mode 100644 src/tests/disabled_functions_ret_type_true.phpt create mode 100644 src/tests/disabled_functions_ret_val.phpt create mode 100644 src/tests/disabled_functions_ret_val_rx.phpt create mode 100644 src/tests/disabled_functions_right_hash.phpt create mode 100644 src/tests/disabled_functions_runtime.phpt create mode 100644 src/tests/disabled_functions_zero_cidr.phpt create mode 100644 src/tests/disabled_option.phpt create mode 100644 src/tests/disabled_user_functions.phpt create mode 100644 src/tests/dump_request.phpt create mode 100644 src/tests/dump_request_invalid_folder.phpt create mode 100644 src/tests/dump_request_too_big.phpt create mode 100644 src/tests/empty_conf.phpt create mode 100644 src/tests/encrypt_cookies.phpt create mode 100644 src/tests/encrypt_cookies2.phpt create mode 100644 src/tests/encrypt_cookies3.phpt create mode 100644 src/tests/encrypt_cookies4.phpt create mode 100644 src/tests/encrypt_cookies_invalid_decryption.phpt create mode 100644 src/tests/encrypt_cookies_invalid_decryption2.phpt create mode 100644 src/tests/encrypt_cookies_invalid_decryption3.phpt create mode 100644 src/tests/encryption_key_only.phpt create mode 100644 src/tests/example_configuration.phpt create mode 100644 src/tests/global_strict.phpt create mode 100644 src/tests/global_strict_disabled.phpt create mode 100644 src/tests/harden_mt_rand.phpt create mode 100644 src/tests/harden_rand.phpt create mode 100644 src/tests/harden_rand_noargs.phpt create mode 100644 src/tests/inexistent_conf_file.phpt create mode 100644 src/tests/loading.phpt create mode 100644 src/tests/noncore_function_hooking.phpt create mode 100644 src/tests/phpinfo_presence.phpt create mode 100644 src/tests/serialize.phpt create mode 100644 src/tests/setcookie.phpt create mode 100644 src/tests/shipped_configuration.phpt create mode 100644 src/tests/unserialize.phpt create mode 100644 src/tests/unserialize_fail.phpt create mode 100644 src/tests/unserialize_sim.phpt create mode 100644 src/tests/upload_validation.phpt create mode 100644 src/tests/upload_validation_invalid.phpt create mode 100644 src/tests/upload_validation_ko.phpt create mode 100644 src/tests/upload_validation_no_exec.phpt create mode 100644 src/tests/upload_validation_nocrash.phpt create mode 100644 src/tests/upload_validation_ok.phpt create mode 100644 src/tweetnacl.c create mode 100644 src/tweetnacl.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..68dfa7ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +*.txt +*.orig +*.gdbhistory +*.swp +.deps +.libs +*.lo +src/tests/*.diff +src/tests/*.exp +src/tests/*.log +src/tests/*.out +src/tests/*.sh +src/tests/*.php +# Files generated by phpize, configure and make +src/autom4te.cache +src/build +src/modules +src/acinclude.m4 +src/aclocal.m4 +src/config.guess +src/config.h +src/config.h.in +src/config.log +src/config.nice +src/config.status +src/config.sub +src/configure +src/configure.in +src/install-sh +src/*.la +src/ltmain.sh +src/libtool +src/Makefile +src/Makefile.fragments +src/Makefile.global +src/Makefile.objects +src/missing +src/mkinstalldirs +src/run-tests.php diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..869f9fbe --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +clean: + make -C src clean + cd src; phpize --clean + +debug: + cd src; phpize + export CFLAGS="-Wall -Wextra -g3 -ggdb -Wno-unused-function"; cd src; ./configure --enable-snuffleupagus --enable-debug + make -C src + TEST_PHP_ARGS='-q' REPORT_EXIT_STATUS=1 make -C src test + +coverage: + cd src; phpize + export CFLAGS="--coverage -fprofile-arcs -ftest-coverage -O0"; export LDFLAGS="--coverage"; cd src; ./configure --enable-snuffleupagus --enable-coverage + make -C src + lcov --base-directory src --directory ./src --zerocounters -q --rc lcov_branch_coverage=1 + rm -Rf src/COV.html + TEST_PHP_ARGS='-q' REPORT_EXIT_STATUS=1 make -C src test + lcov --base-directory ./src --directory src -c -o ./src/COV.info --rc lcov_branch_coverage=1 2>/dev/null 1>/dev/null + lcov --remove src/COV.info '/usr/*' --remove src/COV.info '*tweetnacl.c' -o src/COV.info --rc lcov_branch_coverage=1 2>/dev/null 1>/dev/null + genhtml -o src/COV.html ./src/COV.info --branch-coverage + +tests: joomla + +joomla: + if [ -nd "joomla-cms" ]; then git clone --depth 1 git@github.com:joomla/joomla-cms.git; fi + cd joomla-cms; composer install + cd joomla-cms; libraries/vendor/phpunit/phpunit/phpunit -d extension=./src/modules/snuffleupagus.so + +packages: debian + +debian: + dpkg-buildpackage -i -us -uc -tc -I -rfakeroot diff --git a/README.md b/README.md new file mode 100644 index 00000000..53b9003a --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +[![build +status](https://gitlab.nbs-system.com/secu/snuffleupagus/badges/master/build.svg)](https://gitlab.nbs-system.com/secu/snuffleupagus/commits/master) +[![coverage +report](https://gitlab.nbs-system.com/secu/snuffleupagus/badges/master/coverage.svg)](https://gitlab.nbs-system.com/secu/snuffleupagus/commits/master) +[Documentation]( https://secu.gitlab-pages.nbs-system.com/snuffleupagus/ ) + +# Code style +We're using [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to +ensure a consistent code-style. Please run it with `clang-format -style=google` +before committing, or even better, use a [pre-commit hook](https://github.com/andrewseidl/githook-clang-format) + +# Resources +- The [writeup]( https://github.com/beberlei/whitewashing.de/blob/master/drafts/porting_extension_to_php7.rst) of [tideway profiler](https://github.com/tideways/php-profiler-extension)'s port to php7 +- [Upgrading to php-ng]( https://wiki.php.net/phpng-upgrading ) +- PHP7 [virtual machine](https://nikic.github.io/2017/04/14/PHP-7-Virtual-machine.html) diff --git a/config/default.ini b/config/default.ini new file mode 100644 index 00000000..0f676329 --- /dev/null +++ b/config/default.ini @@ -0,0 +1,54 @@ +# Harden the `chmod` function +sp.disable_functions.function("chmod").param("mode").value_r("^[0-9]{2}[67]$").drop(); +sp.disable_functions.function("chmod").param("mode").value_r("o\\+w$").drop(); + +# Prevent various `mail`-related vulnerabilities +sp.disable_functions.function("mail").param("additional_parameters").value_r("\\-").drop(); + +##Prevent various `include`-related vulnerabilities +sp.disable_functions.function_r("^(?:require|include)_once$").value_r("\\.(?:php|php7|inc|tpl)$").allow(); +sp.disable_functions.function_r("^require|include$").value_r("\\.(?:php|php7|inc|tpl)$").allow(); +sp.disable_functions.function_r("^(?:require|include)_once$").drop(); +sp.disable_functions.function_r("^require|include$").drop(); + +# Prevent `system`-related injections +sp.disable_functions.function("system").param("command").value_r("[$|;&`\\n]").drop(); +sp.disable_functions.function("shell_exec").param("command").value_r("[$|;&`\\n]").drop(); +sp.disable_functions.function("exec").param("command").value_r("[$|;&`\\n]").drop(); +sp.disable_functions.function("proc_open").param("command").value_r("[$|;&`\\n]").drop(); + +# Prevent runtime modification of interesting things +sp.disable_functions.function("ini_set").param("var_name").value("assert.active").drop(); +sp.disable_functions.function("ini_set").param("var_name").value("zend.assertions").drop(); +sp.disable_functions.function("ini_set").param("var_name").value("memory_limit").drop(); +sp.disable_functions.function("ini_set").param("var_name").value("include_path").drop(); +sp.disable_functions.function("ini_set").param("var_name").value("open_basedir").drop(); + +# Detect some backdoors via environnement recon +sp.disable_functions.function("ini_get").param("var_name").value_r("(?:allow_url_fopen|open_basedir|suhosin)").drop(); +sp.disable_functions.function("function_exists").param("function_name").value_r("(?:eval|exec|system)").drop(); +sp.disable_functions.function("is_callable").param("var").value_r("(?:eval|exec|system)").drop(); + +# Ghetto sqli hardening +sp.disable_functions.function_r("mysqli?_query").param("query").value_r("/\\*").drop(); +sp.disable_functions.function_r("mysqli?_query").param("query").value_r("--").drop(); +sp.disable_functions.function_r("mysqli?_query").param("query").value_r("#").drop(); +sp.disable_functions.function_r("mysqli?_query").param("query").value_r(";.*;").drop(); +sp.disable_functions.function_r("mysqli?_query").param("query").value_r("benchmark").drop(); +sp.disable_functions.function_r("mysqli?_query").param("query").value_r("sleep").drop(); +sp.disable_functions.function_r("mysqli?_query").param("query").value_r("information_schema").drop(); +sp.disable_functions.function("PDO::query").param("query").value_r("/\\*").drop(); +sp.disable_functions.function("PDO::query").param("query").value_r("--").drop(); +sp.disable_functions.function("PDO::query").param("query").value_r("#").drop(); +sp.disable_functions.function("PDO::query").param("query").value_r(";.*;").drop(); +sp.disable_functions.function("PDO::query").param("query").value_r("benchmark\\s*\\(").drop(); +sp.disable_functions.function("PDO::query").param("query").value_r("sleep\\s*\\(").drop(); +sp.disable_functions.function("PDO::query").param("query").value_r("information_schema").drop(); + +# Ghetto sqli detection +sp.disable_functions.function_r("mysqli?_query").ret("FALSE").drop(); +sp.disable_functions.function_r("PDO::query").ret("FALSE").drop(); + +#File upload +sp.disable_functions.function("move_uploaded_file").param("destination").value_r("\\.ph").drop(); +sp.disable_functions.function("move_uploaded_file").param("destination").value_r("\\.ht").drop(); diff --git a/config/examples.ini b/config/examples.ini new file mode 100644 index 00000000..d7599fb3 --- /dev/null +++ b/config/examples.ini @@ -0,0 +1,47 @@ +# Restrict system calls to specific file +sp.disable_functions.function("system").filename("update.php").allow(); +sp.disable_functions.function("system").drop(); + + +# Restrict system calls to specific file with a specific hash +sp.disable_functions.function("system").filename("update.php").hash("d27c6c5686bc129716b6aac8dfefe2d519a80eb6cc144e97ad42c728d423eed0").allow(); +sp.disable_functions.function("system").drop(); + + +# AbanteCart 1.2.8 - Multiple SQL Injections +sp.disable_functions.filename("static_pages/index.php").var("_SERVER[PHP_SELF").value_r("\"").drop().alias("XSS"); +sp.disable_functions.filename("core/lib/language_manager.php").function("ALanguageManager>_clone_language_rows").param("from_language").value_r("[^0-9]").drop(); +sp.disable_functions.filename("admin/model/tool/backup.php").function("ModelToolBackup>createBackupTask").param("data[table_list]").value_r("'").drop(); + + +# Redaxo 5.2.0: Remote Code Execution via CSRF +# See for details +sp.disable_functions.filename("redaxo/src/addons/structure/pages/linkmap.php").function("substr").param("string").value_r("\"").drop(); + + +# Guest Post: Vtiger 6.5.0 - SQL Injection +sp.disable_functions.filename("modules/Calendar/Activity.php").function("save_module").param("query").value_r("[^0-9;]").drop(); + + +# The State of Wordpress Security +# All In One WP Security & Firewall +sp.disable_functions.filename("admin/wp-security-dashboard-menu.php").function("render_tab3").var("_REQUEST[tab]]").value_r("\"").drop(); + + +# PHPKit 1.6.6: Code Execution for Privileged Users +sp.disable_functions.filename("pkinc/func/default.php").function("move_uploaded_file").param("destination").value_r("\\.ph\\.+$").drop(); + + +# Coppermine 1.5.42: Second-Order Command Execution +sp.disable_functions.filename("include/imageobject_im.class.php").function("exec").var("CONFIG[im_options]).value_r("[^a-z0-9]").drop(); +sp.disable_functions.filename("forgot_passwd.php").function("cpg_db_query").var("CLEAN[id]").value_r("[^a-z0-9]").drop(); + + +# CVE-2014-1610 - Mediawiki RCE +sp.disable_functions.filename("includes/media/DjVu.php") +sp.disable_functions.filename("includes/media/ImageHandler.php").var("_GET[page]").value_r("[^0-9]").drop() + + +# CVE-2017-1001000 - https://blog.sucuri.net/2017/02/content-injection-vulnerability-wordpress-rest-api.html +sp.disable_functions.filename("wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php").function("register_routes").var("_GET[id]").value_r("[^0-9]").drop(); +sp.disable_functions.filename("wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php").function("register_routes").var("_POST[id]").value_r("[^0-9]").drop(); \ No newline at end of file diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 00000000..6d301b8b --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +snuffleupagus (0.1) UNRELEASED; urgency=medium + + * Initial release. + + -- jvoisin Tue, 04 Jul 2017 17:51:31 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 00000000..f599e28b --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +10 diff --git a/debian/control b/debian/control new file mode 100644 index 00000000..c1cfb929 --- /dev/null +++ b/debian/control @@ -0,0 +1,14 @@ +Source: snuffleupagus +Priority: optional +Maintainer: NBS System +Build-Depends: debhelper (>= 9), php7.0-dev +Standards-Version: 3.9.2 +Homepage: https://snuffleupagus.fr +Section: php +Vcs-Git: https://github.com/nbs-system/snuffleupagus + +Package: snuffleupagus +Architecture: any +Depends: ${misc:Depends} +Description: Hardening for your php7 stack + Snuffleupagus is cool diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 00000000..a7924524 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,8 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: Snuffleupagus +Upstream-Contact: NBS System +Source: https://github.com/nbs-system/snuffleupagus + +Files: * +Copyright: 2017 NBS System +License: LGPL diff --git a/debian/docs b/debian/docs new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/debian/docs @@ -0,0 +1 @@ +README.md diff --git a/debian/rules b/debian/rules new file mode 100755 index 00000000..66bf44a0 --- /dev/null +++ b/debian/rules @@ -0,0 +1,27 @@ +#!/usr/bin/make -f + +DH_VERBOSE = 1 + +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/default.mk + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +%: + dh $@ + +override_dh_auto_clean: + cd ./src; phpize --clean + +override_dh_auto_configure: + cd ./src; phpize + cd ./src; ./configure --enable-snuffleupagus + +override_dh_auto_build: + make -C src + +override_dh_auto_install: + make -C src install + +override_dh_auto_test: + TEST_PHP_ARGS="-q" REPORT_EXIST_STATUS=1 make -C src test diff --git a/debian/watch b/debian/watch new file mode 100644 index 00000000..86028c70 --- /dev/null +++ b/debian/watch @@ -0,0 +1,2 @@ +version=3 +https://github.com/nbs-system/snuffleupagus/tags /nbs-system/snuffleupagus/archive/snuffleupagus-([0-9.]+)\.tar\.(gz|xz|bz2) diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 00000000..73550116 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = Snuffleupagus +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/doc/source/_static/custom.css b/doc/source/_static/custom.css new file mode 100644 index 00000000..1c47d049 --- /dev/null +++ b/doc/source/_static/custom.css @@ -0,0 +1,4 @@ +blockquote { + border-left: 2px solid #999; + padding-left: 20px; +} \ No newline at end of file diff --git a/doc/source/_static/sp.jpg b/doc/source/_static/sp.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0575ca708c849b9d29bcd1659609a3fa50308b62 GIT binary patch literal 36559 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 literal 0 HcmV?d00001 diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 00000000..b2af5f2a --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,178 @@ +# -*- coding: utf-8 -*- +# +# Snuffleupagus documentation build configuration file, created by +# sphinx-quickstart on Tue Jun 27 14:15:46 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) +from datetime import datetime + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +#extensions = ['sphinx.ext.githubpages'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Snuffleupagus' +copyright = u'%d, NBS System' % datetime.now().year +author = u'Sebastien Blot & Julien Voisin' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'0.1' +# The full version, including alpha/beta/rc tags. +release = u'Public Alpha' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = [] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'manni' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# + +html_sidebars = { + '**': [ + 'about.html', + 'navigation.html', + 'relations.html', + 'searchbox.html', + #'donate.html', + ] +} +html_theme_options = { + 'logo': './sp.jpg', + #'description': '
Killing bug-classes in PHP 7, virtual-patching the rest.', + #'fixed_sidebar': True, + 'page_width': '60%', + 'show_powered_by': False, + } + +sidebar_collapse = True + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +#html_logo = './sp.png' + +html_show_sphinx = False + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Snuffleupagusdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'Snuffleupagus.tex', u'Snuffleupagus Documentation', + u'Sebastien Blot \\& Julien Voisin', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'snuffleupagus', u'Snuffleupagus Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'Snuffleupagus', u'Snuffleupagus Documentation', + author, 'Snuffleupagus', 'One line description of project.', + 'Miscellaneous'), +] + + + diff --git a/doc/source/config.rst b/doc/source/config.rst new file mode 100644 index 00000000..8318e7dd --- /dev/null +++ b/doc/source/config.rst @@ -0,0 +1,283 @@ +Configuration +============= + +Since PHP *ini-like* configuration model isn't flexible enough, +Snuffleupagus is using its own format. + +Options are chainable by using dots (``.``), and string parameters +**must** be quoted, while booleans and integers aren't. + +Comments are prefixed either with ``#``, or ``;``. + +Some rules applies in a specific ``function`` (context), on a specific ``variable`` +(data), like ``disable_functions``, others can only be enabled/disabled, like +``harden_random``. + +.. warning:: + + Careful, a wrongly configured Snuffleupagus might break your website. + It's up to you to understand its :doc:`features `, + read the present documentation about how to configure them, + evaluate your threat model, and write your configuration file accordingly. + +The rules are evaluated in the order that they are written, and the **first** one +to match will terminate the evaluation (except for rules in simulation mode). + +Bugclass-killer features +------------------------ + +global_strict +^^^^^^^^^^^^^ +`default: disabled` + +``global_strict`` will enable the `strict `_ mode globally, +forcing PHP to throw a `TypeError `_ +exception if an argument type being passed to a function does not match its corresponding declared parameter type. + +It can either be ``enabled`` or ``disabled`` + +:: + + sp.global_strict.disable(); + sp.global_strict.enable(); + +harden_random +^^^^^^^^^^^^^ + * `default: enabled` + * `more `__ + +``harden_random`` will silently replace the insecure `rand `_ +and `mt_rand `_ functions with +the secure PRNG `random_int `_. + +It can either be ``enabled`` or ``disabled``. + +:: + + sp.harden_random.enable(); + sp.harden_random.disable(); + +.. _config_global: + +global +^^^^^^ + +This configuration variable contain parameters that are used by other ones: + +- ``secret_key``: A secret key used by various cryptographic features, + like `cookies protection `__ or `unserialize protection `__, + so do make sure that it's random and long enough. + You can generate it with something like this: ``head -c 256 /dev/urandom | tr -dc 'a-zA-Z0-9'``. + +:: + + sp.global.secret_key("44239bd400aa82e125337c9d4eb8315767411ccd"); + +unserialize_hmac +^^^^^^^^^^^^^^^^ + * `default: disabled` + * `more `__ + +``unserialize_hmac`` will add integrity check to ``unserialize`` calls, preventing +abritrary code execution in their context. + +:: + + sp.unserialize_hmac.enable(); + sp.unserialize_hmac.disable(); + + +auto_cookie_secure +^^^^^^^^^^^^^^^^^^ + * `default: disabled` + * `more `__ + +``auto_cookie_secure`` will automatically mark cookies as `secure `_ +when the web page is requested over HTTPS. + +It can either be ``enabled`` or ``disabled``. + +:: + + sp.auto_cookie_secure.enable(); + sp.auto_cookie_secure.disable(); + +cookie_encryption +^^^^^^^^^^^^^^^^^ + * `default: disabled` + * `more `__ + +.. warning:: + + To use this feature, you **must** set the :ref:`global.secret_key ` variable. + This design decision prevents attacker from + `trivially bruteforcing `_ + session cookies. + +``cookie_secure`` will activate transparent encryption of specific cookies. + +It can either be ``enabled`` or ``disabled``. + +:: + + sp.cookie_encryption.cookie("my_cookie_name"); + sp.cookie_encryption.cookie("another_cookie_name"); + + +readonly_exec +^^^^^^^^^^^^^ + * `default: disabled` + +``readonly_exec`` will prevent the execution of writable PHP files. + +It can either be ``enabled`` or ``disabled``. + +:: + + sp.readonly_exec.enable(); + +upload_validation +^^^^^^^^^^^^^^^^^ + * `default: disabled` + * `more `__ + +``upload_validation`` will call a given script upon a file upload, with the path +to the file being uploaded as argument, and various information about it in the environment: + +* ``SP_FILENAME``: the name of the uploaded file +* ``SP_FILESIZE``: the size of the file being uploaded +* ``SP_REMOTE_ADDR``: the ip address of the uploader +* ``SP_CURRENT_FILE``: the current file being executed + +This feature can be used, for example, to check if an uploaded file contains php +code, with something like `vld `_ +(``php -d vld.execute=0 -d vld.active=1 -d extension=vld.so yourfile.php``). + +The upload will be **allowed** if the script return the value ``0``. Every other +value will prevent the file from being uploaded. + +:: + + sp.upload_validation.script("/var/www/is_valid_php.py").enable(); + + +disable_xxe +^^^^^^^^^^^ + * `default: enabled` + * `more `__ + +``disable_xxe`` will prevent XXE attacks by disabling the loading of external entities (``libxml_disable_entity_loader``) in the XML parser. + +:: + + sp.disable_xxe.enable(); + + +Virtual-patching +---------------- + +Snuffleupagus provides virtual-patching, via the ``disable_functions`` directive, allowing you to stop or control dangerous behaviours. +Admitting you have a call to ``system()`` that lacks proper user-input validation, thus leading to an **RCE**, this might be the right tool. + +:: + + # Allow `id.php` to restrict system() calls to `id` + sp.disable_functions.function("system").filename("id.php").param("cmd").value("id").allow(); + sp.disable_functions.function("system").filename("id.php").drop() + +Of course, this is a trivial example, and a lot can be achieved with this feature, as you will see below. + + +Filters +^^^^^^^ + +- ``alias(:str)``: human-readable description of the rule +- ``cidr(ip/mask:str)``: match on the client's `cidr `_ +- ``filename(name:str)``: match in the file ``name`` +- ``filename_r(regexp:str)``: the file name matching the ``regexp`` +- ``function(name:str)``: match on function ``name`` +- ``function_r(regexp:str)``: the function matching the ``regexp`` +- ``hash(:str)``: match on the file's `sha256 `_ sum +- ``param(name:str)``: match on the function's parameter ``name`` +- ``param_r(regexp:str)``: match on the function's parameter ``regexp`` +- ``param_type(type:str)``: match on the function's parameter ``type`` +- ``ret(value:str)``: match on the function's return ``value`` +- ``ret_r(regexp:str)``: match with a ``regexp`` on the function's return +- ``ret_type(type_name:str)``: match on the ``type_name`` of the function's return value +- ``value(:str)``: match on a litteral value +- ``value_r(:regexp)``: match on a value matching the ``regexp`` +- ``var(name:str)``: match on a **local variable** ``name`` + +The ``type`` must be one of the following values: + +- ``FALSE``: for boolean false +- ``TRUE``: for boolean true +- ``NULL``: for the **null** value +- ``LONG``: for a long (also know as ``integer``) value +- ``DOUBLE``: for a **double** (also known as ``float``) value +- ``STRING``: for a string +- ``OBJECT``: for a object +- ``ARRAY``: for an array +- ``RESOURCE``: for a resource + +Actions +^^^^^^^ + +- ``allow()``: **allow** the request if the rule matches +- ``drop()``: **drop** the request if the rule matches +- ``dump(directory:str)``: dump the request in the ``directory`` if it matches the rule +- ``simulation()``: enabled the simulation mode + +Details +^^^^^^^ + +The ``function`` filter is able to do various dereferencing: + +- ``function("AwesomeClass::my_method")`` will match in the method ``my_method`` in the class ``AwesomeClass`` + +The ``param`` filter is also able to do some dereferencing: + +- ``param(foo[bar])`` will get match on the value corresponding to the ``bar`` key in the hashtable ``foo``. + Remember that in PHP, almost every data structure is a hashtable. You can of course nest this like + ``param(foo[bar][baz][batman])``. +- The ``var`` filter will walk the calltrace until it finds the variable's name, or the end of it, + allowing to match on global variables: ``.var("_GET[param]")`` will match on the GET parameter ``param``. + +For clarity's sake, the presence of the ``allow`` or ``drop`` action is **mandatory**. + +.. warning:: + + When you're writing rules, please do keep in mind that the **order matters**. + For example, if you're denying a call to ``system()`` and then allowing it in a + more narrowed way later, the call will be denied, + because it'll match the deny first. + +If you're paranoid, we're providing a php script to automatically generate +hash of files containing dangerous functions, +and blacklisting them everywhere else. + +Examples +^^^^^^^^ + +Evaluation order of rules +""""""""""""""""""""""""" + +The following rules will: + +1. Allow calls to ``system("id")`` +2. Issue a trace in the logs on calls to ``system`` with its parameters starting with ``ping``, + and pursuing evaluation of the remaining rules. +3. Drop calls to ``system``. + + +:: + + sp.disable_functions.function("system").param("cmd").value("id").allow(); + sp.disable_functions.function("system").param("cmd").value_r("^ping").drop().simulation(); + sp.disable_functions.function("system").param("cmd").drop(); + +Miscellaneous examples +"""""""""""""""""""""" + +.. literalinclude:: ../../config/examples.ini + :language: python \ No newline at end of file diff --git a/doc/source/download.rst b/doc/source/download.rst new file mode 100644 index 00000000..161937f1 --- /dev/null +++ b/doc/source/download.rst @@ -0,0 +1,16 @@ +Download +======== + +Debian +------ + + +Ubuntu +------ + + +Source code +----------- + +:: + git clone https://github.com/nbs-system.com/snuffleupagus diff --git a/doc/source/faq.rst b/doc/source/faq.rst new file mode 100644 index 00000000..07aba330 --- /dev/null +++ b/doc/source/faq.rst @@ -0,0 +1,196 @@ +FAQ +=== + +General +------- + +What is Snuffleupagus? +"""""""""""""""""""""" + +Snuffleupagus is a `PHP7+ `_ +module designed to drastically raising the cost of attacks against website, +by killing entire bug classes, and also providing a powerful virtual-patching system, +allowing administrator to fix specific vulnerabilities without having to touch the PHP code. + + +Where does the name *Snuffeupagus* comes from? +"""""""""""""""""""""""""""""""""""""""""""""" + + Aloysius Snuffleupagus, more commonly known as Mr. Snuffleupagus, Snuffleupagus + or Snuffy for short, is one of the characters on Sesame Street, + the educational television program for young children. + + He was created as a woolly mammoth, without tusks or (visible) ears, + and has a long thick pointed tail, similar in shape to that of a dinosaur + or other reptile. He has long thick brown hair and a trunk, or "snuffle", + that drags along the ground. He is Big Bird's best friend and + has a baby sister named Alice. He also attends "Snufflegarten". + + --- `Wikipedia `_ + + +Why is Snuffleupagus called Snuffleupagus? +"""""""""""""""""""""""""""""""""""""""""" + +Like PHP's `ElePHPant `_, +we thought that using an elephant as a mascot would be a great idea. + + +Why did you write Snuffleupagus? +"""""""""""""""""""""""""""""""" + +We're working for `NBS System `__, +a web hosting company (meaning that we're dealing with PHP code all day long), +with a strong focus on security. We do have hardening +(kernel, `WAF `_, `IDS `_, …) +below the web stack, but most of the time, when a website is compromised, +it's either to send ads, spam, deface it, steal data, … +This is why we need to harden the website itself too, but we can't touch its +source code. + +Why not Suhosin? +"""""""""""""""" + +We're huge fans of `Suhosin `_, unfortunately: + +- it doesn't work very well on PHP 7 +- it has some oudated features and misses new ones +- it doesn't cope very well with our various industrialization needs +- it has some shortcomings by design + +We're using the `disable_function `_ +directive, but unfortunately, it doesn't provide enough usable granularity (guess how many CMS are using +``system`` to do various mandatory maintenance tasks…). + +This is why we decided to write our own hardening module, in the spirit of Suhosin, +via virtual-patching support, and other cool new features. + +What license is Snuffleupagus under and why? +"""""""""""""""""""""""""""""""""""""""""""" + +Snuffleupagus is licensed under the `LGPL `_, +and is developed by the fine people from `NBS System `__. + +We chose the LGPL because we don't care that much how you're using Snuffleupagus, +but we'd like to force people to make their improvements/contributions +available to everyone. + +Should I use Snuffleupagus? +""""""""""""""""""""""""""" + +Yes. + +Even if you're not using the virtual-patching capabilities, Snuffleupagus comes +with various passive features that won't break your website while killing numerous vulnerabilities. + +Please keep in mind that you are not only protecting yourself and your users/customers, +but also other people on the internet that might be attacked by your server if +it becomes compromised. + +How mature is this project? +""""""""""""""""""""""""""" + +This project was floating around since early 2016, and we did the first commit +the 28ᵗʰ of December of the same year. We're currently in a private alpha phase, +finding and fixing as much bugs as possible with the help of friends. + +Are you saying that PHP isn't secure? +""""""""""""""""""""""""""""""""""""" + +We don't like PHP's approach of security; namely (sometimes) adding warnings +in the documentation and trusting the developer to not do any mistake, +instead of focusing on the root cause, and killing the +bug class one for all. + +Moreover, it seems that the current attitude toward security in the PHP world +is to `blame the user `_ instead of acknowledging +issues, as stated in their `documentation `_. +We do think that an security issue that "requires the use of code or settings known to be insecure" +is still a security issue, and should be treated as such. + +Installation and configuration +------------------------------ + +Can snuffleupagus break my application? +""""""""""""""""""""""""""""""""""""""" +Yes. + +Some options won't break anything, like ``harden_rand``, but some like ``global_strict`` +or overly-restrictives virtual-patching rules might pretty well break your website. +It's up to you to configure Snuffleupaggus accordingly to your needs. + +You can also enable the ``simulation`` mode on features that you're not sure about, +to see what would snuffleupagus do to your application, before activating them for good. + +How can I find out the problem when my application breaks? +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +By checking the logs; Snuffleupagus systematically prefix them with ``[snuffleupagus]``. + + +Does Snuffleupagus run on Windows? +"""""""""""""""""""""""""""""""""" +No idea. + + +Will Snuffleupagus run on my old PHP 5? +""""""""""""""""""""""""""""""""""""""" +No. + +Since PHP5 `will be deprecated at the end of 2018 `_, +you should think about moving to PHP7 anyway. You can (and should) use +`Suhosin `_ in the meantime. + +Help and support +---------------- + +I found a security issue +"""""""""""""""""""""""" +If you believe you have found a security issue affecting Snuffleupagus, +then we would be more than happy to hear from you! + +We promise to treat any reported issue seriously and, +if the investigation confirms it affects Snuffleupagus, +to patch it within a reasonable time, +release a public announcement that describes the issue, +discuss potential impact of the vulnerability, +reference applicable patches or workarounds, +and credit the discoverer. + +Please send it us a mail to the ``snuffleupagus`` user, +on ``nbs-system.com``. + +I found a bug. How can I report it? +""""""""""""""""""""""""""""""""""" +We do have an issue tracker on `Github `_. +Please make sure to include as much information as possible when reporting your issue, +such as your operating system, your version of PHP 7, your version of snuffleupagus, +your logs, the problematic php code, the request, a brief description, … long story short, +give us everything that you can. + +Where can I find even more help? +"""""""""""""""""""""""""""""""" +The :doc:`configuration page ` might be what you're looking for. +If you're adventurous, you can also check the `issue tracker `_ +(make sure to check the closed issues too). + +I need professional support for my company. +""""""""""""""""""""""""""""""""""""""""""" +Contact `NBS System `_. + +Unimplemented mitigations and abandoned ideas +--------------------------------------------- + +Contant time comparisons +"""""""""""""""""""""""" +We didn't manage to perform time-based side-channel attacks on strings +against real world PHP application, and the results that we gathered on +tailored test cases weren't concluding: for simplicity's sake, we chose +to not implement a mitigation against this class of attacks. + +We would be happy to be proven wrong, and reconsider implementing this feature, +if someone can manage to get better results than us. + +The possibility of having this natively in PHP has +`been discussed `_, +but as 2017, nothing has been merged yet. diff --git a/doc/source/features.rst b/doc/source/features.rst new file mode 100644 index 00000000..89cd756d --- /dev/null +++ b/doc/source/features.rst @@ -0,0 +1,352 @@ +Features +======== + +Snuffleupagus has a lot of features that can be divided in two main categories: bug-classes +killers and virtual-patching. The first category provides primitives to kill various +bug families (like arbitrary code execution via ``unserialize`` for example) or rise the +cost of exploitation, the second one is a highly configurable system to patch functions in php itself. + +Bug classes killed +------------------ + +``system`` injections +^^^^^^^^^^^^^^^^^^^^^ + +The ``system`` function execute an external program and displays the output. +It's used to interract with various external tools, like file-format converters for example. +Unfortunately, passing user-controlled parameters to it often leads to an arbitrary command execution. + + When allowing user-supplied data to be passed to this function, + use `escapeshellarg()` or `escapeshellcmd()` to ensure that users cannot trick + the system into executing arbitrary commands. + + --- `The PHP documentation about system `_ + +We're kind of killing it by filtering the ``$``, ``|``, ``;``, ````` and ``&`` chars in our +default configuration, making it a lot harder for an attacker to inject arbitrary commands. + +This family of vulnerabilities lead to various CVE, like: + +- `CVE-2017-7981 `_: Authenticated remote code execution on Tuleap +- `CVE-2014-4688 `_: Authenticated remote code execution on pfSense +- `CVE-2014-1610 `_: Unauthenticated remote code execution on DokuWiki +- `CVE-2013-3630 `_: Authenticated remote code execution on Moodle +- Every single shitty `modem/router/switch/IoT `_. + + +``mail``-related injections +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This vulnerability is known `since 2011 `_, +and was popularized by `RIPS `_ in 2016. +The last flag of the `mail` function can be used to pass various parameters to +the underlying binary used to send emails: this can lead to an arbitrary file write, +often meaning an arbitrary code execution. + + The ``additional_parameters`` parameter can be used to pass additional flags + as command line options to the program configured to be used when sending mail + + --- `The PHP documentation about mail `_ + +We're killing it by preventing any extra options in additional_parameters. + +This family of vulnerabilities lead to various CVE, like: + +- `CVE-2017-7692 `_: Authenticated remote code execution in SquirrelMail +- `CVE-2016-10074 `_: remote code execution in SwiftMailer +- `CVE-2016-10033 `_: remote code execution in PHPMailer +- `CVE-2016-9920 `_: Unauthenticated remote code execution in Roundcube + +Session-cookie stealing via XSS +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The goto payload for XSS is often to steal cookies. +Like *Suhosin*, we are encrypting the cookies with a secret key, the IP of the user +and its user-agent. This means that an attacker with an XSS won't be able to use +the stolen cookie, since he (often) can't spoof the IP address of the user. + +This feature is roughly the same than the `Suhosin one `_. + +Users behind the same IP address but with different browsers won't be able to use each other stolen cookies, +except if they can manage to guess the user agent. This isn't especially difficult, +but an invalid decryption will leave a trace in the logs. + +Finally, having a secret server-side key will prevent anyone (even the user himself) +from reading the content of the cookie, reducing the impact of an application storing sensitive data client-side. + +The encryption is done via the [tweetnacl library](https://tweetnacl.cr.yp.to/), +thus using curve25519, xsalsa20 and poly1305 for the encryption. We chose this +library because of its portability, simplicity and reduced size (a single `.h` and +`.c` file.). + +Remote code execution via file-upload +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Some PHP applications allows users to upload contents, like avatars for a forum. +Unfortunately, sometimes, content validation isn't implemented properly (if at all), +meaning arbitrary file upload, often leading, contrary to what the documentation is saying, +to an arbitrary code execution. + + Not validating which file you operate on may mean that users can *access sensitive information* in other directories. + + --- `The PHP documentation about file uploads `_ + +We're killing it, like Suhosin, by automatically calling a script upon file upload, +if it returns something else than ``0``, the file will be removed (or stored in a quarantine, +for further analysis). + +We're recommending to use the `vld `_ project +inside the script to ensure the file doesn't contain any valid PHP code, with something like this: + +:: + + $ php -d vld.execute=0 -d vld.active=1 -d extension=vld.so $file + +Unserialize-related magic +^^^^^^^^^^^^^^^^^^^^^^^^^ + +PHP is able to *serialize* arbitrary objects, to easily store them. +Unfortunately, it's often possible to gain arbitrary code execution upon deserialization +of user-supplied serialized objects. + + Do not pass untrusted user input to ``unserialize()`` regardless of the options value of allowed_classes. + Unserialization can result in code being loaded and executed due to object instantiation and autoloading, + and a malicious user may be able to exploit this. + + --- `The PHP documentation about serialize `_ + +We're killing it by exploiting the fact that PHP will discard any garbage found at the end of a serialized object, +allowing us to simply append a `HMAC `_ +at the end of strings generated by the ``serialize``, +hence guaranteeing that any object deserialized came from the application, +and wasn't tampered with, + +We're not encrypting it, like we do with the cookies, +allowing this feature to be disabled (or switch into leaning mode) +without the need to invalidate any data. + +.. warning:: + + This feature can't be deployed on websites that already stored serialized + objects (ie. in database), since they are missing the HMAC, and thus will be detected as + an attack. If you're in this situation, you should use this feature with the + ``simulation`` mode, and switch it off once you don't have any messages in your + logs. + +A nice side-effect of this feature is that it'll defeat various memory corruption +issues related to the complexity of ``unserialize``'s implementation, +and the amount of control if provides to an attacker, like `CVE-2016-9137, CVE-2016-9138 `_, +`2016-7124 `_, `CVE-2016-5771 and CVE-2016-5773 `_, … + +This family of vulnerabilities lead to various CVE, like: + +- `CVE-2016-???? `_: Unauthenticated remote code execution in Observium (leading to remote root) +- `CVE-2016-5726 `_: Unauthenticated remote code execution in Simple Machines Forums +- `CVE-2016-4010 `_: Unauthenticated remote code execution in Magento +- `CVE-2017-2641 `_: Unauthenticated remote code execution in Moodle +- `CVE-2015-8562 `_: Unauthenticated remote code execution in Joomla +- `CVE-2015-7808 `_: Unauthenticated remote code execution in vBulletin +- `CVE-2014-1691 `_: Unauthenticated remote code execution in Horde +- `CVE-2012-5692 `_: Unauthenticated remote code execution in IP.Board + + + +Weak-PRNG via rand/mt_rand +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The functions ``rand`` and ``mt_rand`` are often used to generate random numbers used +in sensitive context, like password generation, token creation, … +Unfortunately, as said in the documentation, the quality of their entropy is low, +leading to the generation of guessable values. + + This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. + + --- `The PHP documentation about rand `_ + +We're addressing this issue by replacing every call to ``rand`` and ``mt_rand`` with +a call to the ``random_int``, a `CSPRNG `_. + +It's worth noting that the PHP documentation contains the following warning: + + ``min`` ``max`` range must be within the range ``getrandmax()``. i.e. ``(max - min) <= getrandmax()``. + Otherwise, ``rand()`` may return poor-quality random numbers. + + --- `The PHP documentation about rand `_ + +This is of course addressed as well by the ``harden_rand`` feature. + +.. warning:: + + Activating this feature will raise an `Error `_ + exception if ``min`` is superior to ``max``, while the default dehaviour is simply to swap them. + +This family of vulnerabilities lead to various CVE, like: + +- `CVE-2015-5267 `_: Unauthenticated accounts takeover in in Moodle +- `CVE-2014-9624 `_: Captcha bypass in MantisBT +- `CVE-2014-6412 `_: Unauthenticated account takeover in Wordpress +- `CVE-2015-???? `_: Unauthenticated accounts takeover in Concrete5 +- `CVE-2013-6386 `_: Unauthenticated accounts takeover in Drupal +- `CVE-2010-???? `_: Unauthenticated accounts takeover in MyBB +- `CVE-2008-4102 `_: Unauthenticated accounts takeover in Joomla +- `CVE-2006-0632 `_: Unauthenticated account takeover in phpBB + +XXE +^^^ + +Despite the documentation saying nothing about this class of vulnerabilities, +`XML eXternal Entitiy `_ (XXE) are often leading to arbitrary file reading, SSRF, and sometimes even arbitrary +code execution. + +XML documents can contain a `Document Type Definition `_ (DTD), +enabling definition of XML entities. It's possible to define an (external) entity by an +URI, that the parser will access, and embed its content back into the document +for further processing. + +For example, providing an url like ``file:///etc/passwd`` will read +this file's content, and since it's not valid XML, the application +will spit it out in an error message, thus leaking its content. + +We're killing this class of vulnerabilities by calling +the `libxml_disable_entity_loader `_ +function with its parameter set to ``true`` at startup, +and then *nop'ing* it, so it won't do anything if ever called again. + +This family of vulnerabilities lead to various CVE, like: + +- `CVE-2015-5161 `_: Unauthenticated arbitrary file disclosure on Magento +- `CVE-2014-8790 `_: Unauthenticated remote code execution in GetSimple CMS +- `CVE-2011-4107 `_: Authenticated local file disclosure in PHPMyAdmin + + +Cookie stealing via HTTP MITM +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While it's possible to set the ``secure`` flag on cookies to prevent them from being +transmitted over HTTP, and only allow its transmission over HTTPS. +Snuffleupagus can automatically set this flag if the client is accessing the +website over a secure connection. + +This behaviour is suggested in the documentation: + + On the server-side, it's on the programmer to send this kind of cookie only + on secure connection (e.g. with respect to ``$_SERVER["HTTPS"]``). + + --- `The PHP documentation about setcookie `_ + + +Exploitation, post-exploitation and general hardening +----------------------------------------------------- + +Virtual-patching +^^^^^^^^^^^^^^^^ + +PHP itself exposes a number of functions that might be considered **dangerous** and that have limited legitimate use cases. +``system()``, ``exec()``, ``dlopen()`` - for example - fall into this category. By default, PHP only allows to globally disable some functions. + + +However, (ie. ``system()``) they might have legitimate use cases in processes such as self upgrade etc., making it impossible to effectively +disable them - at the risk of breaking critical features. + +SnuffleuPagus allows the user to restrict usage of specific functions per files, or per +files with a matching (sha256) hash, thus allowing the use of such functions **only** in the intended places. + +Furthermore, running the `following script `_ will generate an hash and line-based whitelist +of dangerous functions, droping them everywhere else: + + +.. literalinclude:: ../../scripts/generate_rules.php + :language: php + + +The intent is to make post-exploitation process (such as backdooring of legitimate code, or RAT usage) a lot harder for the attacker. + + +Global strict mode +^^^^^^^^^^^^^^^^^^ + +By default, PHP will coerce values of the wrong type into the expected one +if possible. For example, if a function expecting an integer is given a string, +it will be coerced in an integer. + +PHP7 introduced a **strict mode**, in which variables won't be coerced anymore, +and a `TypeError `_ exception will +be raised if the types aren't matching. +`Scalar type declarations `_ +are optional, but you don't have to used them in your code to benefit from them, +since every internal function from php has them. + +This option provide a switch to globally activate this strict mode, +helping to uncover vulnerabilities like the classical +`strcmp bypass `_, +and various other types mismatch. + +This feature is largely inspired from the +`autostrict `_ module from `krakjoe `_. + + +Preventing execution of writable PHP files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If an attacker manages to upload an arbitrary file or to modify an existing one, +odds are that (thanks to the default `umask `_) +this file is writable by the PHP process. + +Snuffleupagus can prevent the execution of this kind of files. A good practise +would be to use a different user to run PHP than for administrating the website, +and using this feature to lock this up. + + + +Dumping capabilities +^^^^^^^^^^^^^^^^^^^^ +It's possible to apply the ``dump(:str)`` filter to any virtual-patching rule, +to dump the complete web request, along with the filename and the corresponding +line number. By using the *right* set of restrictive rules (or by using the +*overly* restrictives ones in ``simulation`` mode), you might be able +to gather interesting vulnerabilities used against your website. + + +Misc low-hanging fruits in the default configuration file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Snuffleupagus is shipping with a default configuration file, containing +various examples and ideas of things that you might want to enable (or not). + +Available functions recon +""""""""""""""""""""""""" + +After compromising a website, most of the time, the attacker does some recon +within its webshell, to check which functions are available to execute arbitrary code, +since it's not uncommon for some web-hoster to disable things like ``system`` or ``passthru``, +or to check if mitigations are enabled, like ``open_basedir``. +This behaviour can be detected by preventing the execution of functions like ``ini_get`` +or ``is_callable`` with *suspicious* parameters. + +``chmod`` hardening +""""""""""""""""""" + +Some PHP applications are using broad rights when using the ``chmod`` function, +like the infamous ``chmod(777)`` command, effectively making the file writable by everyone. +Snuffleupagus is preventing this kind of behaviour by restricting the parameters +than can be passer to ``chmod``. + +Arbitrary file inclusion hardening +"""""""""""""""""""""""""""""""""" + +Arbitrary file inclusion is a common vulnerability, that might be detected +by preventing the use of anything else than a whitelist of extensions in calls +to ``include`` or ``require``. + +*Cheap* SQL injections detection +"""""""""""""""""""""""""""""""" + +In some SQL injections, attackers might need to use comments, a feature that is +often not used in production system, so it might be a good idea to filter +queries that contains some. The same filtering idea can be used against +SQL functions that are frequently used in SQL injections, like ``sleep``, ``benchmark`` +or strings like ``version_info``. + +Still about SQL injections, if a function performing a query returns ``FALSE`` +(indicating an error), it might be useful to dump the request for further analysis. + diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 00000000..8a9c340b --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,30 @@ +Snuffleupagus +============= + +Snuffleupagus is a `PHP 7+ `_ module designed to drastically raising the cost of +attacks against website, by killing entire bug classes, and also providing a +powerful virtual-patching system, allowing administrator to fix specific +vulnerabilities and audit suspicious behaviours without having to touch the PHP code. + +Documentation +------------- + +.. toctree:: + :maxdepth: 2 + + features + installation + config + download + faq + papers + +Greetings +--------- +We would like to thank the following people: + +- `Suhosin `_, for paving the way. +- The people behind the `RIPS `_ scanner for their ground breaking work +- `NBS System `_, for creating and open-sourcing this piece of software +- `Websec.fr `_, for keeping our interesting vulnerabilities alive +- Web developers around the world, for being so imaginative diff --git a/doc/source/installation.rst b/doc/source/installation.rst new file mode 100644 index 00000000..779008d6 --- /dev/null +++ b/doc/source/installation.rst @@ -0,0 +1,33 @@ +Installation +============ + +Snuffleupagus is tested against various PHP 7+ versions: XXX + +Manual installation +------------------- + +Depending on the system, we might already offer binary packages. +You can check our :doc:`download`. In that case you only need to activate +the extension inside your ``php.ini`` and to configure it. + + +Quickstart +^^^^^^^^^^ + +:: + + git clone https://github.com/nbs-system/snuffleupagus + cd snuffleupagus + phpize + ./configure + make + make install + +This should install ``snuffleupagus.so`` file in your extension directory. The final step is adding a load directive to ``php.ini``:: + + extension=snuffleupagus.so + +Upgrading +--------- + +Upgrading the Snuffleupagus is as simple as recompiling it (or using a binary), replacing the file and restarting your webserver. diff --git a/doc/source/papers.rst b/doc/source/papers.rst new file mode 100644 index 00000000..d028b141 --- /dev/null +++ b/doc/source/papers.rst @@ -0,0 +1,16 @@ +Propaganda +========== + +This pages lists various mentions, articles and presentations about Snuffleupagus. + +Talks +----- + +- `BerlinSide0x08 `_ - `Php7 Nightmares `_ - 2017-05-28 +- `Hack.lu `_ - soon™ - 2017-10-18 +- `BlackAlps `_ - soon™ - 2017-11-16 + +Articles +-------- + +- `Killing php bug classes at berlinsides `_ - 2017-06-05 \ No newline at end of file diff --git a/scripts/generate_rules.php b/scripts/generate_rules.php new file mode 100644 index 00000000..e286ef12 --- /dev/null +++ b/scripts/generate_rules.php @@ -0,0 +1,43 @@ + $object){ + if (FALSE === in_array (pathinfo($name, PATHINFO_EXTENSION), $extensions, true)) { + continue; + } + + $hash = ''; + $file_content = file_get_contents($name); + + foreach(token_get_all($file_content) as $token) { + if ($token[0] != 319) { + continue; + } + + if (in_array($token[1], $functions_blacklist, true)) { + if ('' === $hash) { + $hash = hash('sha256', $file_content); + } + echo 'sp.disable_function.function("' . $token[1] . '").filename("' . $name . '").hash("' . $hash . '").allow();' . "\n"; + } + } +} +foreach($functions_blacklist as $fun) { + echo 'sp.disable_function.function("' . $fun . '").drop();' . "\n"; + +} diff --git a/src/bench/bench.php b/src/bench/bench.php new file mode 100644 index 00000000..5f771803 --- /dev/null +++ b/src/bench/bench.php @@ -0,0 +1,422 @@ +0)) { + $im=$re*$im*2+$imc; + $re=$re2-$im2+$rec; + $re2=$re*$re; + $im2=$im*$im; + $color=$color-1; + } + if ( $color==0 ) { + print "_"; + } else { + print "#"; + } + } + print "
"; + flush(); + } +} + +/****/ + +function mandel2() { + $b = " .:,;!/>)|&IH%*#"; + //float r, i, z, Z, t, c, C; + for ($y=30; printf("\n"), $C = $y*0.1 - 1.5, $y--;){ + for ($x=0; $c = $x*0.04 - 2, $z=0, $Z=0, $x++ < 75;){ + for ($r=$c, $i=$C, $k=0; $t = $z*$z - $Z*$Z + $r, $Z = 2*$z*$Z + $i, $z=$t, $k<5000; $k++) + if ($z*$z + $Z*$Z > 500000) break; + echo $b[$k%16]; + } + } +} + +/****/ + +function Ack($m, $n){ + if($m == 0) return $n+1; + if($n == 0) return Ack($m-1, 1); + return Ack($m - 1, Ack($m, ($n - 1))); +} + +function ackermann($n) { + $r = Ack(3,$n); + print "Ack(3,$n): $r\n"; +} + +/****/ + +function ary($n) { + for ($i=0; $i<$n; $i++) { + $X[$i] = $i; + } + for ($i=$n-1; $i>=0; $i--) { + $Y[$i] = $X[$i]; + } + $last = $n-1; + print "$Y[$last]\n"; +} + +/****/ + +function ary2($n) { + for ($i=0; $i<$n;) { + $X[$i] = $i; ++$i; + $X[$i] = $i; ++$i; + $X[$i] = $i; ++$i; + $X[$i] = $i; ++$i; + $X[$i] = $i; ++$i; + + $X[$i] = $i; ++$i; + $X[$i] = $i; ++$i; + $X[$i] = $i; ++$i; + $X[$i] = $i; ++$i; + $X[$i] = $i; ++$i; + } + for ($i=$n-1; $i>=0;) { + $Y[$i] = $X[$i]; --$i; + $Y[$i] = $X[$i]; --$i; + $Y[$i] = $X[$i]; --$i; + $Y[$i] = $X[$i]; --$i; + $Y[$i] = $X[$i]; --$i; + + $Y[$i] = $X[$i]; --$i; + $Y[$i] = $X[$i]; --$i; + $Y[$i] = $X[$i]; --$i; + $Y[$i] = $X[$i]; --$i; + $Y[$i] = $X[$i]; --$i; + } + $last = $n-1; + print "$Y[$last]\n"; +} + +/****/ + +function ary3($n) { + for ($i=0; $i<$n; $i++) { + $X[$i] = $i + 1; + $Y[$i] = 0; + } + for ($k=0; $k<1000; $k++) { + for ($i=$n-1; $i>=0; $i--) { + $Y[$i] += $X[$i]; + } + } + $last = $n-1; + print "$Y[0] $Y[$last]\n"; +} + +/****/ + +function fibo_r($n){ + return(($n < 2) ? 1 : fibo_r($n - 2) + fibo_r($n - 1)); +} + +function fibo($n) { + $r = fibo_r($n); + print "$r\n"; +} + +/****/ + +function hash1($n) { + for ($i = 1; $i <= $n; $i++) { + $X[dechex($i)] = $i; + } + $c = 0; + for ($i = $n; $i > 0; $i--) { + if ($X[dechex($i)]) { $c++; } + } + print "$c\n"; +} + +/****/ + +function hash2($n) { + for ($i = 0; $i < $n; $i++) { + $hash1["foo_$i"] = $i; + $hash2["foo_$i"] = 0; + } + for ($i = $n; $i > 0; $i--) { + foreach($hash1 as $key => $value) $hash2[$key] += $value; + } + $first = "foo_0"; + $last = "foo_".($n-1); + print "$hash1[$first] $hash1[$last] $hash2[$first] $hash2[$last]\n"; +} + +/****/ + +function gen_random ($n) { + global $LAST; + return( ($n * ($LAST = ($LAST * IA + IC) % IM)) / IM ); +} + +function heapsort_r($n, &$ra) { + $l = ($n >> 1) + 1; + $ir = $n; + + while (1) { + if ($l > 1) { + $rra = $ra[--$l]; + } else { + $rra = $ra[$ir]; + $ra[$ir] = $ra[1]; + if (--$ir == 1) { + $ra[1] = $rra; + return; + } + } + $i = $l; + $j = $l << 1; + while ($j <= $ir) { + if (($j < $ir) && ($ra[$j] < $ra[$j+1])) { + $j++; + } + if ($rra < $ra[$j]) { + $ra[$i] = $ra[$j]; + $j += ($i = $j); + } else { + $j = $ir + 1; + } + } + $ra[$i] = $rra; + } +} + +function heapsort($N) { + global $LAST; + + define("IM", 139968); + define("IA", 3877); + define("IC", 29573); + + $LAST = 42; + for ($i=1; $i<=$N; $i++) { + $ary[$i] = gen_random(1); + } + heapsort_r($N, $ary); + printf("%.10f\n", $ary[$N]); +} + +/****/ + +function mkmatrix ($rows, $cols) { + $count = 1; + $mx = array(); + for ($i=0; $i<$rows; $i++) { + for ($j=0; $j<$cols; $j++) { + $mx[$i][$j] = $count++; + } + } + return($mx); +} + +function mmult ($rows, $cols, $m1, $m2) { + $m3 = array(); + for ($i=0; $i<$rows; $i++) { + for ($j=0; $j<$cols; $j++) { + $x = 0; + for ($k=0; $k<$cols; $k++) { + $x += $m1[$i][$k] * $m2[$k][$j]; + } + $m3[$i][$j] = $x; + } + } + return($m3); +} + +function matrix($n) { + $SIZE = 30; + $m1 = mkmatrix($SIZE, $SIZE); + $m2 = mkmatrix($SIZE, $SIZE); + while ($n--) { + $mm = mmult($SIZE, $SIZE, $m1, $m2); + } + print "{$mm[0][0]} {$mm[2][3]} {$mm[3][2]} {$mm[4][4]}\n"; +} + +/****/ + +function nestedloop($n) { + $x = 0; + for ($a=0; $a<$n; $a++) + for ($b=0; $b<$n; $b++) + for ($c=0; $c<$n; $c++) + for ($d=0; $d<$n; $d++) + for ($e=0; $e<$n; $e++) + for ($f=0; $f<$n; $f++) + $x++; + print "$x\n"; +} + +/****/ + +function sieve($n) { + $count = 0; + while ($n-- > 0) { + $count = 0; + $flags = range (0,8192); + for ($i=2; $i<8193; $i++) { + if ($flags[$i] > 0) { + for ($k=$i+$i; $k <= 8192; $k+=$i) { + $flags[$k] = 0; + } + $count++; + } + } + } + print "Count: $count\n"; +} + +/****/ + +function strcat($n) { + $str = ""; + while ($n-- > 0) { + $str .= "hello\n"; + } + $len = strlen($str); + print "$len\n"; +} + +/*****/ + +function getmicrotime() +{ + $t = gettimeofday(); + return ($t['sec'] + $t['usec'] / 1000000); +} + +function start_test() +{ + ob_start(); + return getmicrotime(); +} + +function end_test($start, $name) +{ + global $total; + $end = getmicrotime(); + ob_end_clean(); + $total += $end-$start; + $num = number_format($end-$start,3); + $pad = str_repeat(" ", 24-strlen($name)-strlen($num)); + + echo $name.$pad.$num."\n"; + ob_start(); + return getmicrotime(); +} + +function total() +{ + global $total; + $pad = str_repeat("-", 24); + echo $pad."\n"; + $num = number_format($total,3); + $pad = str_repeat(" ", 24-strlen("Total")-strlen($num)); + echo "Total".$pad.$num."\n"; +} + +$t0 = $t = start_test(); +simple(); +$t = end_test($t, "simple"); +simplecall(); +$t = end_test($t, "simplecall"); +simpleucall(); +$t = end_test($t, "simpleucall"); +simpleudcall(); +$t = end_test($t, "simpleudcall"); +mandel(); +$t = end_test($t, "mandel"); +mandel2(); +$t = end_test($t, "mandel2"); +ackermann(7); +$t = end_test($t, "ackermann(7)"); +ary(50000); +$t = end_test($t, "ary(50000)"); +ary2(50000); +$t = end_test($t, "ary2(50000)"); +ary3(2000); +$t = end_test($t, "ary3(2000)"); +fibo(30); +$t = end_test($t, "fibo(30)"); +hash1(50000); +$t = end_test($t, "hash1(50000)"); +hash2(500); +$t = end_test($t, "hash2(500)"); +heapsort(20000); +$t = end_test($t, "heapsort(20000)"); +matrix(20); +$t = end_test($t, "matrix(20)"); +nestedloop(12); +$t = end_test($t, "nestedloop(12)"); +sieve(30); +$t = end_test($t, "sieve(30)"); +strcat(200000); +$t = end_test($t, "strcat(200000)"); +total($t0, "Total"); +?> diff --git a/src/bench/micro_bench.php b/src/bench/micro_bench.php new file mode 100644 index 00000000..70525882 --- /dev/null +++ b/src/bench/micro_bench.php @@ -0,0 +1,358 @@ +b; + } + } + + function write_prop($n) { + for ($i = 0; $i < $n; ++$i) { + $this->b = 0; + } + } + + function assign_add_prop($n) { + for ($i = 0; $i < $n; ++$i) { + $this->b += 2; + } + } + + function pre_inc_prop($n) { + for ($i = 0; $i < $n; ++$i) { + ++$this->b; + } + } + + function pre_dec_prop($n) { + for ($i = 0; $i < $n; ++$i) { + --$this->b; + } + } + + function post_inc_prop($n) { + for ($i = 0; $i < $n; ++$i) { + $this->b++; + } + } + + function post_dec_prop($n) { + for ($i = 0; $i < $n; ++$i) { + $this->b--; + } + } + + function isset_prop($n) { + for ($i = 0; $i < $n; ++$i) { + $x = isset($this->b); + } + } + + function empty_prop($n) { + for ($i = 0; $i < $n; ++$i) { + $x = empty($this->b); + } + } + + function g() { + } + + function call($n) { + for ($i = 0; $i < $n; ++$i) { + $this->g(); + } + } + + function read_const($n) { + for ($i = 0; $i < $n; ++$i) { + $x = $this::TEST; + } + } + +} + +function read_static($n) { + for ($i = 0; $i < $n; ++$i) { + $x = Foo::$a; + } +} + +function write_static($n) { + for ($i = 0; $i < $n; ++$i) { + Foo::$a = 0; + } +} + +function isset_static($n) { + for ($i = 0; $i < $n; ++$i) { + $x = isset(Foo::$a); + } +} + +function empty_static($n) { + for ($i = 0; $i < $n; ++$i) { + $x = empty(Foo::$a); + } +} + +function call_static($n) { + for ($i = 0; $i < $n; ++$i) { + Foo::f(); + } +} + +function create_object($n) { + for ($i = 0; $i < $n; ++$i) { + $x = new Foo(); + } +} + +define('TEST', null); + +function read_const($n) { + for ($i = 0; $i < $n; ++$i) { + $x = TEST; + } +} + +function read_auto_global($n) { + for ($i = 0; $i < $n; ++$i) { + $x = $_GET; + } +} + +$g_var = 0; + +function read_global_var($n) { + for ($i = 0; $i < $n; ++$i) { + $x = $GLOBALS['g_var']; + } +} + +function read_hash($n) { + $hash = array('test' => 0); + for ($i = 0; $i < $n; ++$i) { + $x = $hash['test']; + } +} + +function read_str_offset($n) { + $str = "test"; + for ($i = 0; $i < $n; ++$i) { + $x = $str[1]; + } +} + +function issetor($n) { + $val = array(0,1,2,3,4,5,6,7,8,9); + for ($i = 0; $i < $n; ++$i) { + $x = $val ?: null; + } +} + +function issetor2($n) { + $f = false; $j = 0; + for ($i = 0; $i < $n; ++$i) { + $x = $f ?: $j + 1; + } +} + +function ternary($n) { + $val = array(0,1,2,3,4,5,6,7,8,9); + $f = false; + for ($i = 0; $i < $n; ++$i) { + $x = $f ? null : $val; + } +} + +function ternary2($n) { + $f = false; $j = 0; + for ($i = 0; $i < $n; ++$i) { + $x = $f ? $f : $j + 1; + } +} + +/*****/ + +function empty_loop($n) { + for ($i = 0; $i < $n; ++$i) { + } +} + +function getmicrotime() +{ + $t = gettimeofday(); + return ($t['sec'] + $t['usec'] / 1000000); +} + +function start_test() +{ + ob_start(); + return getmicrotime(); +} + +function end_test($start, $name, $overhead = null) +{ + global $total; + global $last_time; + $end = getmicrotime(); + ob_end_clean(); + $last_time = $end-$start; + $total += $last_time; + $num = number_format($last_time,3); + $pad = str_repeat(" ", 24-strlen($name)-strlen($num)); + if (is_null($overhead)) { + echo $name.$pad.$num."\n"; + } else { + $num2 = number_format($last_time - $overhead,3); + echo $name.$pad.$num." ".$num2."\n"; + } + ob_start(); + return getmicrotime(); +} + +function total() +{ + global $total; + $pad = str_repeat("-", 24); + echo $pad."\n"; + $num = number_format($total,3); + $pad = str_repeat(" ", 24-strlen("Total")-strlen($num)); + echo "Total".$pad.$num."\n"; +} + +const N = 5000000; + +$t0 = $t = start_test(); +empty_loop(N); +$t = end_test($t, 'empty_loop'); +$overhead = $last_time; +simpleucall(N); +$t = end_test($t, 'func()', $overhead); +simpleudcall(N); +$t = end_test($t, 'undef_func()', $overhead); +simpleicall(N); +$t = end_test($t, 'int_func()', $overhead); +Foo::read_static(N); +$t = end_test($t, '$x = self::$x', $overhead); +Foo::write_static(N); +$t = end_test($t, 'self::$x = 0', $overhead); +Foo::isset_static(N); +$t = end_test($t, 'isset(self::$x)', $overhead); +Foo::empty_static(N); +$t = end_test($t, 'empty(self::$x)', $overhead); +read_static(N); +$t = end_test($t, '$x = Foo::$x', $overhead); +write_static(N); +$t = end_test($t, 'Foo::$x = 0', $overhead); +isset_static(N); +$t = end_test($t, 'isset(Foo::$x)', $overhead); +empty_static(N); +$t = end_test($t, 'empty(Foo::$x)', $overhead); +Foo::call_static(N); +$t = end_test($t, 'self::f()', $overhead); +call_static(N); +$t = end_test($t, 'Foo::f()', $overhead); +$x = new Foo(); +$x->read_prop(N); +$t = end_test($t, '$x = $this->x', $overhead); +$x->write_prop(N); +$t = end_test($t, '$this->x = 0', $overhead); +$x->assign_add_prop(N); +$t = end_test($t, '$this->x += 2', $overhead); +$x->pre_inc_prop(N); +$t = end_test($t, '++$this->x', $overhead); +$x->pre_dec_prop(N); +$t = end_test($t, '--$this->x', $overhead); +$x->post_inc_prop(N); +$t = end_test($t, '$this->x++', $overhead); +$x->post_dec_prop(N); +$t = end_test($t, '$this->x--', $overhead); +$x->isset_prop(N); +$t = end_test($t, 'isset($this->x)', $overhead); +$x->empty_prop(N); +$t = end_test($t, 'empty($this->x)', $overhead); +$x->call(N); +$t = end_test($t, '$this->f()', $overhead); +$x->read_const(N); +$t = end_test($t, '$x = Foo::TEST', $overhead); +create_object(N); +$t = end_test($t, 'new Foo()', $overhead); +read_const(N); +$t = end_test($t, '$x = TEST', $overhead); +read_auto_global(N); +$t = end_test($t, '$x = $_GET', $overhead); +read_global_var(N); +$t = end_test($t, '$x = $GLOBALS[\'v\']', $overhead); +read_hash(N); +$t = end_test($t, '$x = $hash[\'v\']', $overhead); +read_str_offset(N); +$t = end_test($t, '$x = $str[0]', $overhead); +issetor(N); +$t = end_test($t, '$x = $a ?: null', $overhead); +issetor2(N); +$t = end_test($t, '$x = $f ?: tmp', $overhead); +ternary(N); +$t = end_test($t, '$x = $f ? $f : $a', $overhead); +ternary2(N); +$t = end_test($t, '$x = $f ? $f : tmp', $overhead); +total($t0, "Total"); diff --git a/src/config.m4 b/src/config.m4 new file mode 100644 index 00000000..aba355c0 --- /dev/null +++ b/src/config.m4 @@ -0,0 +1,35 @@ +dnl $Id$ +dnl config.m4 for extension snuffleupagus + +sources="snuffleupagus.c sp_config.c sp_config_utils.c sp_harden_rand.c" +sources="$sources sp_unserialize.c sp_utils.c sp_disable_xxe.c sp_list.c" +sources="$sources sp_disabled_functions.c sp_execute.c sp_upload_validation.c" +sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c" +sources="$sources sp_config_keywords.c sp_compile.c" + +PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support, +[ --enable-snuffleupagus Enable snuffleupagus support]) + +PHP_ARG_ENABLE(coverage, whether to enable coverage support, +[ --enable-coverage Enable coverage support]) + +PHP_ARG_ENABLE(debug, whether to enable debug messages, +[ --enable-debug Enable debug messages]) + +CFLAGS="$CFLAGS -lpcre" +CFLAGS="$CFLAGS -D_DEFAULT_SOURCE=1 -std=c99" +CFLAGS="$CFLAGS -Wall -Wextra -Wno-unused-parameter" + +if test "$PHP_DEBUG" = "yes"; then + AC_DEFINE(SP_DEBUG, 1, [Wether you want to enable debug messages]) +fi + +if test "$PHP_SNUFFLEUPAGUS" != "no"; then + if test "$PHP_COVERAGE" != "no"; then + CFLAGS="$CFLAGS --coverage -fprofile-arcs -ftest-coverage" + LDFLAGS="$LDFLAGS --coverage" + PHP_NEW_EXTENSION(snuffleupagus, $sources, $ext_shared,-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -g -fprofile-arcs -ftest-coverage -lgcov) + else + PHP_NEW_EXTENSION(snuffleupagus, $sources, $ext_shared,-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) + fi +fi diff --git a/src/config.w32 b/src/config.w32 new file mode 100644 index 00000000..a0197c1f --- /dev/null +++ b/src/config.w32 @@ -0,0 +1,13 @@ +// $Id$ +// vim:ft=javascript + +// If your extension references something external, use ARG_WITH +// ARG_WITH("snuffleupagus", "for snuffleupagus support", "no"); + +// Otherwise, use ARG_ENABLE +// ARG_ENABLE("snuffleupagus", "enable snuffleupagus support", "no"); + +if (PHP_SNUFFLEUPAGUS != "no") { + EXTENSION("snuffleupagus", "snuffleupagus.c", PHP_EXTNAME_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +} + diff --git a/src/php_snuffleupagus.h b/src/php_snuffleupagus.h new file mode 100644 index 00000000..e7a3d599 --- /dev/null +++ b/src/php_snuffleupagus.h @@ -0,0 +1,71 @@ +#ifndef PHP_SNUFFLEUPAGUS_H +#define PHP_SNUFFLEUPAGUS_H + +#define PHP_SNUFFLEUPAGUS_VERSION "0.1" +#define PHP_SNUFFLEUPAGUS_EXTNAME "snuffleupagus" +#define PHP_SNUFFLEUPAGUS_AUTHOR "NBS System" +#define PHP_SNUFFLEUPAGUS_URL "https://github.com/nbs-system/snuffleupagus" +#define PHP_SNUFFLEUPAGUS_COPYRIGHT "LGPLv2" + +#include +#include + +#include +#include +#include + +#include "SAPI.h" +#include "ext/standard/info.h" +#include "php.h" +#include "php_ini.h" +#include "zend_hash.h" +#include "zend_string.h" +#include "zend_extensions.h" + +#include "sp_list.h" +#include "sp_compile.h" +#include "sp_config.h" +#include "sp_config_utils.h" +#include "sp_config_keywords.h" +#include "sp_cookie_encryption.h" +#include "sp_disable_xxe.h" +#include "sp_disabled_functions.h" +#include "sp_execute.h" +#include "sp_harden_rand.h" +#include "sp_network_utils.h" +#include "sp_unserialize.h" +#include "sp_upload_validation.h" +#include "sp_utils.h" + +extern zend_module_entry snuffleupagus_module_entry; +#define phpext_snuffleupagus_ptr &snuffleupagus_module_entry + +#ifdef PHP_WIN32 +#define PHP_SNUFFLEUPAGUS_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define PHP_SNUFFLEUPAGUS_API __attribute__((visibility("default"))) +#else +#define PHP_SNUFFLEUPAGUS_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +ZEND_BEGIN_MODULE_GLOBALS(snuffleupagus) +sp_config config; +HashTable *disabled_functions_hook; +HashTable *sp_internal_functions_hook; +ZEND_END_MODULE_GLOBALS(snuffleupagus) + +#define SNUFFLEUPAGUS_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(snuffleupagus, v) + +#if defined(ZTS) && defined(COMPILE_DL_SNUFFLEUPAGUS) +ZEND_TSRMLS_CACHE_EXTERN() +#endif + +PHP_FUNCTION(check_disabled_function); + +static inline void sp_terminate() { zend_bailout(); } + +#endif /* PHP_SNUFFLEUPAGUS_H */ diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c new file mode 100644 index 00000000..52b975ed --- /dev/null +++ b/src/snuffleupagus.c @@ -0,0 +1,222 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php_snuffleupagus.h" + +#ifndef ZEND_EXT_API +#define ZEND_EXT_API ZEND_DLEXPORT +#endif + +static PHP_INI_MH(OnUpdateConfiguration); +static inline int zend_auto_start(zend_extension *extension); +static inline void sp_op_array_handler(zend_op_array *op); + +ZEND_EXTENSION(); + +ZEND_DLEXPORT int sp_zend_startup(zend_extension *extension) { + return zend_startup_module(&snuffleupagus_module_entry); +} + +static inline void sp_op_array_handler(zend_op_array *op) { + if (NULL == op->filename) { + return; + } else { + op->fn_flags |= ZEND_ACC_STRICT_TYPES; + } +} + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) + +PHP_INI_BEGIN() +PHP_INI_ENTRY("sp.configuration_file", "", PHP_INI_SYSTEM, + OnUpdateConfiguration) +PHP_INI_END() + +ZEND_DLEXPORT zend_extension zend_extension_entry = { + PHP_SNUFFLEUPAGUS_EXTNAME, + PHP_SNUFFLEUPAGUS_VERSION, + PHP_SNUFFLEUPAGUS_AUTHOR, + PHP_SNUFFLEUPAGUS_URL, + PHP_SNUFFLEUPAGUS_COPYRIGHT, + sp_zend_startup, + NULL, + NULL, /* activate_func_t */ + NULL, /* deactivate_func_t */ + NULL, /* message_handler_func_t */ + sp_op_array_handler,//zend_global_strict, /* op_array_handler_func_t */ + NULL, /* statement_handler_func_t */ + NULL, /* fcall_begin_handler_func_t */ + NULL, /* fcall_end_handler_func_t */ + NULL, /* op_array_ctor_func_t */ + NULL, /* op_array_dtor_func_t */ + STANDARD_ZEND_EXTENSION_PROPERTIES}; + +/* Uncomment this function if you have INI entries +static void php_snuffleupagus_init_globals(zend_snuffleupagus_globals +*snuffleupagus_globals) +{ + snuffleupagus_globals->global_value = 0; + snuffleupagus_globals->global_string = NULL; +} +*/ + +PHP_GINIT_FUNCTION(snuffleupagus) { +#define SP_INIT(F) F = pecalloc(sizeof(*F), 1, 1); +#define SP_INIT_HT(F) \ + F = pemalloc(sizeof(*F), 1); \ + zend_hash_init(F, 10, NULL, NULL, 1); + + SP_INIT_HT(snuffleupagus_globals->disabled_functions_hook); + SP_INIT_HT(snuffleupagus_globals->sp_internal_functions_hook); + + SP_INIT(snuffleupagus_globals->config.config_unserialize); + SP_INIT(snuffleupagus_globals->config.config_random); + SP_INIT(snuffleupagus_globals->config.config_readonly_exec); + SP_INIT(snuffleupagus_globals->config.config_global_strict); + SP_INIT(snuffleupagus_globals->config.config_auto_cookie_secure); + SP_INIT(snuffleupagus_globals->config.config_snuffleupagus); + SP_INIT(snuffleupagus_globals->config.config_disable_xxe); + SP_INIT(snuffleupagus_globals->config.config_upload_validation); + SP_INIT(snuffleupagus_globals->config.config_disabled_functions); + SP_INIT(snuffleupagus_globals->config.config_disabled_functions_ret); + SP_INIT(snuffleupagus_globals->config.config_cookie_encryption); + SP_INIT(snuffleupagus_globals->config.config_regexp_inclusion); + + snuffleupagus_globals->config.config_regexp_inclusion->regexp_inclusion = sp_new_list(); + snuffleupagus_globals->config.config_disabled_functions->disabled_functions = sp_new_list(); + snuffleupagus_globals->config.config_disabled_functions_ret->disabled_functions = sp_new_list(); + + SP_INIT_HT(snuffleupagus_globals->config.config_cookie_encryption->names); + +#undef SP_INIT +#undef SP_INIT_HT +} + +PHP_MINIT_FUNCTION(snuffleupagus) { + REGISTER_INI_ENTRIES(); + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(snuffleupagus) { +#define FREE_HT(F) \ + zend_hash_destroy(SNUFFLEUPAGUS_G(F)); \ + pefree(SNUFFLEUPAGUS_G(F), 1); + + FREE_HT(disabled_functions_hook); + FREE_HT(config.config_cookie_encryption->names); + +#undef FREE_HT + + pefree(SNUFFLEUPAGUS_G(config.config_unserialize), 1); + pefree(SNUFFLEUPAGUS_G(config.config_random), 1); + pefree(SNUFFLEUPAGUS_G(config.config_readonly_exec), 1); + pefree(SNUFFLEUPAGUS_G(config.config_global_strict), 1); + pefree(SNUFFLEUPAGUS_G(config.config_auto_cookie_secure), 1); + pefree(SNUFFLEUPAGUS_G(config.config_snuffleupagus), 1); + pefree(SNUFFLEUPAGUS_G(config.config_disable_xxe), 1); + pefree(SNUFFLEUPAGUS_G(config.config_upload_validation), 1); + pefree(SNUFFLEUPAGUS_G(config.config_cookie_encryption), 1); + + sp_list_free(SNUFFLEUPAGUS_G(config.config_disabled_functions->disabled_functions)); + pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions), 1); + sp_list_free(SNUFFLEUPAGUS_G(config.config_disabled_functions_ret->disabled_functions)); + pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions_ret), 1); + + UNREGISTER_INI_ENTRIES(); + + return SUCCESS; +} + +PHP_RINIT_FUNCTION(snuffleupagus) { +#if defined(COMPILE_DL_SNUFFLEUPAGUS) && defined(ZTS) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + if (NULL != SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key) { + if (NULL != SNUFFLEUPAGUS_G(config).config_cookie_encryption->names) { + zend_hash_apply_with_arguments( + Z_ARRVAL(PG(http_globals)[TRACK_VARS_COOKIE]), decrypt_cookie, 0); + } + } + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(snuffleupagus) { return SUCCESS; } + +PHP_MINFO_FUNCTION(snuffleupagus) { + php_info_print_table_start(); + php_info_print_table_header(2, "snuffleupagus support", "enabled"); + php_info_print_table_end(); + + /* Remove comments if you have entries in php.ini + DISPLAY_INI_ENTRIES(); + */ +} + +static PHP_INI_MH(OnUpdateConfiguration) { + TSRMLS_FETCH(); + + if (!new_value || !new_value->len) { + return FAILURE; + } + + if (sp_parse_config(new_value->val) != SUCCESS) { + return FAILURE; + } + + if (SNUFFLEUPAGUS_G(config).config_random->enable) { + hook_rand(); + } + if (SNUFFLEUPAGUS_G(config).config_upload_validation->enable) { + hook_upload(); + } + if (SNUFFLEUPAGUS_G(config).config_disable_xxe->enable == 0) { + hook_libxml_disable_entity_loader(); + } + hook_disabled_functions(); + hook_execute(); + + if (NULL != SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key) { + if (SNUFFLEUPAGUS_G(config).config_unserialize->enable) { + hook_serialize(); + } + hook_cookies(); + } + + if (true == SNUFFLEUPAGUS_G(config).config_global_strict->enable) { + if (!zend_get_extension(PHP_SNUFFLEUPAGUS_EXTNAME)) { + zend_extension_entry.startup = NULL; + zend_register_extension(&zend_extension_entry, NULL); + } + // This is needed to implement the global strict mode + CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; + } + + return SUCCESS; +} + +const zend_function_entry snuffleupagus_functions[] = {PHP_FE_END}; + +zend_module_entry snuffleupagus_module_entry = + {STANDARD_MODULE_HEADER, + PHP_SNUFFLEUPAGUS_EXTNAME, + snuffleupagus_functions, + PHP_MINIT(snuffleupagus), + PHP_MSHUTDOWN(snuffleupagus), + PHP_RINIT(snuffleupagus), + PHP_RSHUTDOWN(snuffleupagus), + PHP_MINFO(snuffleupagus), + PHP_SNUFFLEUPAGUS_VERSION, + PHP_MODULE_GLOBALS(snuffleupagus), + PHP_GINIT(snuffleupagus), + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX}; + +#ifdef COMPILE_DL_SNUFFLEUPAGUS +#ifdef ZTS +ZEND_TSRMLS_CACHE_DEFINE() +#endif +ZEND_GET_MODULE(snuffleupagus) +#endif diff --git a/src/snuffleupagus.php b/src/snuffleupagus.php new file mode 100644 index 00000000..b373a531 --- /dev/null +++ b/src/snuffleupagus.php @@ -0,0 +1,21 @@ +"; + +if(!extension_loaded('snuffleupagus')) { + dl('snuffleupagus.' . PHP_SHLIB_SUFFIX); +} +$module = 'snuffleupagus'; +$functions = get_extension_funcs($module); +echo "Functions available in the test extension:$br\n"; +foreach($functions as $func) { + echo $func."$br\n"; +} +echo "$br\n"; +$function = 'confirm_' . $module . '_compiled'; +if (extension_loaded($module)) { + $str = $function($module); +} else { + $str = "Module $module is not compiled into PHP"; +} +echo "$str\n"; +?> diff --git a/src/sp_compile.c b/src/sp_compile.c new file mode 100644 index 00000000..2902377e --- /dev/null +++ b/src/sp_compile.c @@ -0,0 +1,53 @@ +#include "php_snuffleupagus.h" + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus); + +static zend_op_array *(*orig_compile_file)(zend_file_handle *, int); +static zend_op_array *(*orig_compile_string)(zval *, char *); + +zend_op_array *sp_compile_file(zend_file_handle *file_handle, int type) { + zend_op_array *ret = orig_compile_file(file_handle, type); + + const sp_node_t* config = SNUFFLEUPAGUS_G(config).config_disabled_functions->disabled_functions; + + while (config && config->data) { + const char* function_name = ((sp_disabled_function*)config->data)->function; + const pcre* function_name_regexp = ((sp_disabled_function*)config->data)->r_function; + + sp_log_err("checking for %s", function_name); + // EG(function_table)->arData[count - 1].val.value.func->internal_function.handler = PHP_FN(check_disabled_function); + + if (function_name) { + if (HOOK_FUNCTION(function_name, disabled_functions_hook, PHP_FN(check_disabled_function), true) == SUCCESS) { + sp_log_err("Successfully hooked %s", function_name); + } + break; + } else if (function_name_regexp) { + sp_log_err("error", "We'll hook regard later."); + } + config = config->next; + } + + return ret; +} + +zend_op_array *sp_compile_string(zval *source_string, char *filename) { + zend_op_array *ret = orig_compile_string(source_string, filename); + sp_log_err("in compile_string : filename is :%s", filename); + return ret; +} + + +int hook_compile(void) { + TSRMLS_FETCH(); + + /* zend_compile_file is used to compile php file */ + orig_compile_file = zend_compile_file; + zend_compile_file = sp_compile_file; + + /* zend_compile_string is used to compile php string */ + orig_compile_string = zend_compile_string; + zend_compile_string = sp_compile_string; + + return SUCCESS; +} \ No newline at end of file diff --git a/src/sp_compile.h b/src/sp_compile.h new file mode 100644 index 00000000..538ff524 --- /dev/null +++ b/src/sp_compile.h @@ -0,0 +1,6 @@ +#ifndef SP_COMPILE_H +#define SP_COMPILE_H + +int hook_compile(void); + +#endif /* SP_COMPILE_H */ \ No newline at end of file diff --git a/src/sp_config.c b/src/sp_config.c new file mode 100644 index 00000000..f73347dc --- /dev/null +++ b/src/sp_config.c @@ -0,0 +1,193 @@ +#include +#include +#include + +#include "php_snuffleupagus.h" + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) + +sp_config_tokens const sp_func[] = { + {.func = parse_unserialize, .token = SP_TOKEN_UNSERIALIZE_HMAC}, + {.func = parse_random, .token = SP_TOKEN_HARDEN_RANDOM}, + {.func = parse_disabled_functions, .token = SP_TOKEN_DISABLE_FUNC}, + {.func = parse_readonly_exec, .token = SP_TOKEN_READONLY_EXEC}, + {.func = parse_global_strict, .token = SP_TOKEN_GLOBAL_STRICT}, + {.func = parse_upload_validation, .token = SP_TOKEN_UPLOAD_VALIDATION}, + {.func = parse_cookie_encryption, .token = SP_TOKEN_COOKIE_ENCRYPTION}, + {.func = parse_global, .token = SP_TOKEN_GLOBAL}, + {.func = parse_auto_cookie_secure, .token = SP_TOKEN_AUTO_COOKIE_SECURE}, + {.func = parse_disable_xxe, .token = SP_TOKEN_DISABLE_XXE}, + {NULL, NULL}}; + +/* Top level keyword parsing */ + +static int parse_line(char *line) { + char *ptr = line; + + while (*ptr == ' ' || *ptr == '\t') { + ++ptr; + } + + if (!*ptr || *ptr == '#' || *ptr == ';') { + return 0; + } + + if (strncmp(ptr, SP_TOKEN_BASE, strlen(SP_TOKEN_BASE))) { + sp_log_err("config", "Invalid configuration prefix for '%s'.", line); + return -1; + } + ptr += strlen(SP_TOKEN_BASE); + + for (size_t i = 0; sp_func[i].func; i++) { + if (!strncmp(sp_func[i].token, ptr, strlen(sp_func[i].token))) { + return sp_func[i].func(ptr + strlen(sp_func[i].token)); + } + } + sp_log_err("config", "Invalid configuration section '%s'.", line); + return -1; +} + +/* keyword parsing */ +int parse_empty(char *restrict line, char *restrict keyword, void *retval) { + *(bool *)retval = true; + return 0; +} + +int parse_int(char *restrict line, char *restrict keyword, void *retval) { + size_t consumed = 0; + char *value = get_param(&consumed, line, SP_TYPE_INT, keyword); + if (value) { + sscanf(value, "%ud", (uint32_t *)retval); + pefree(value, 1); + return consumed; + } else { + sp_log_err("error", "%s) is expecting a valid integer.", keyword); + return -1; + } +} + +int parse_php_type(char *restrict line, char *restrict keyword, void *retval) { + size_t consumed = 0; + char *value = get_param(&consumed, line, SP_TYPE_STR, keyword); + if (value) { + if (0 == strcasecmp("undef", value)) { + *(sp_php_type*)retval = SP_PHP_TYPE_UNDEF; + } else if (0 == strcasecmp("null", value)) { + *(sp_php_type*)retval = SP_PHP_TYPE_NULL; + } else if (0 == strcasecmp("true", value)) { + *(sp_php_type*)retval = SP_PHP_TYPE_TRUE; + } else if (0 == strcasecmp("false", value)) { + *(sp_php_type*)retval = SP_PHP_TYPE_FALSE; + } else if (0 == strcasecmp("long", value)) { + *(sp_php_type*)retval = SP_PHP_TYPE_LONG; + } else if (0 == strcasecmp("double", value)) { + *(sp_php_type*)retval = SP_PHP_TYPE_DOUBLE; + } else if (0 == strcasecmp("string", value)) { + *(sp_php_type*)retval = SP_PHP_TYPE_STRING; + } else if (0 == strcasecmp("array", value)) { + *(sp_php_type*)retval = SP_PHP_TYPE_ARRAY; + } else if (0 == strcasecmp("object", value)) { + *(sp_php_type*)retval = SP_PHP_TYPE_OBJECT; + } else if (0 == strcasecmp("resource", value)) { + *(sp_php_type*)retval = SP_PHP_TYPE_RESOURCE; + } else if (0 == strcasecmp("reference", value)) { + *(sp_php_type*)retval = SP_PHP_TYPE_REFERENCE; + } else { + pefree(value, 1); + sp_log_err("error", "%s) is expecting a valid php type ('false', 'true'," + " 'array'. 'object', 'long', 'double', 'null', 'resource', 'reference'," + " 'undef').", keyword); + return -1; + } + pefree(value, 1); + return consumed; + } else { + return -1; + } +} + +int parse_str(char *restrict line, char *restrict keyword, void *retval) { + char *value = NULL; + + size_t consumed = 0; + value = get_param(&consumed, line, SP_TYPE_STR, keyword); + if (value) { + *(char **)retval = value; + return consumed; + } + return -1; +} + +int parse_cidr(char *restrict line, char *restrict keyword, void *retval) { + size_t consumed = 0; + char *value = get_param(&consumed, line, SP_TYPE_STR, keyword); + sp_cidr *cidr = pecalloc(sizeof(sp_cidr), 1, 1); + + if (value) { + if (-1 == get_ip_and_cidr(value, cidr)) { + return -1; + } + *(sp_cidr **)retval = cidr; + return consumed; + } else { + sp_log_err("config", "%s doesn't contain a valid cidr.", line); + return -1; + } +} + +int parse_regexp(char *restrict line, char *restrict keyword, void *retval) { + /* TODO: Do we want to use pcre_study? + * (http://www.pcre.org/original/doc/html/pcre_study.html) + * maybe not: http://sljit.sourceforge.net/pcre.html*/ + size_t consumed = 0; + char *value = get_param(&consumed, line, SP_TYPE_STR, keyword); + + if (value) { + const char *pcre_error; + int pcre_error_offset; + pcre *compiled_re = pcre_compile(value, PCRE_CASELESS, &pcre_error, + &pcre_error_offset, NULL); + if (NULL == compiled_re) { + sp_log_err("config", "Failed to compile '%s': %s.", value, pcre_error); + } else { + *(pcre **)retval = compiled_re; + return consumed; + } + } + char *closing_paren = strchr(line, ')'); + if (NULL != closing_paren) { + closing_paren[0] = '\0'; + } + sp_log_err("config", "'%s)' is expecting a valid regexp, and not '%s'.", + keyword, line); + return -1; +} + +int sp_parse_config(const char *conf_file) { + FILE *fd = fopen(conf_file, "r"); + char *lineptr = NULL; + size_t n = 0; + + if (fd == NULL) { + sp_log_err("config", "Could not open configuration file %s : %s", conf_file, + strerror(errno)); + return FAILURE; + } + + while (getline(&lineptr, &n, fd) > 0) { + /* We trash the terminal `\n`. This simplify the display of logs. */ + if (lineptr[strlen(lineptr) - 1] == '\n') { + lineptr[strlen(lineptr) - 1] = '\0'; + } + if (parse_line(lineptr) == -1) { + fclose(fd); + free(lineptr); + return FAILURE; + } + free(lineptr); + lineptr = NULL; + n = 0; + } + fclose(fd); + return SUCCESS; +} diff --git a/src/sp_config.h b/src/sp_config.h new file mode 100644 index 00000000..54ec2cc8 --- /dev/null +++ b/src/sp_config.h @@ -0,0 +1,206 @@ +#ifndef SP_CONFIG_H +#define SP_CONFIG_H + +#include +#include +#include + +typedef enum { + SP_TYPE_STR = 0, + SP_TYPE_REGEXP, + SP_TYPE_INT, + SP_TYPE_EMPTY +} sp_type; + +typedef enum { + SP_PHP_TYPE_UNDEF = IS_UNDEF, + SP_PHP_TYPE_NULL = IS_NULL, + SP_PHP_TYPE_FALSE = IS_FALSE, + SP_PHP_TYPE_TRUE = IS_TRUE, + SP_PHP_TYPE_LONG = IS_LONG, + SP_PHP_TYPE_DOUBLE = IS_DOUBLE, + SP_PHP_TYPE_STRING = IS_STRING, + SP_PHP_TYPE_ARRAY = IS_ARRAY, + SP_PHP_TYPE_OBJECT = IS_OBJECT, + SP_PHP_TYPE_RESOURCE = IS_RESOURCE, + SP_PHP_TYPE_REFERENCE = IS_REFERENCE +} sp_php_type; + +typedef struct { + int ip_version; + union { + struct in_addr ipv4; + struct in6_addr ipv6; + } ip; + uint8_t mask; +} sp_cidr; + +typedef struct { char *encryption_key; } sp_config_encryption_key; + +typedef struct { + bool enable; + bool simulation; +} sp_config_readonly_exec; + +typedef struct { bool enable; } sp_config_global_strict; + +typedef struct { bool enable; } sp_config_random; + +typedef struct { bool enable; } sp_config_auto_cookie_secure; + +typedef struct { bool enable; } sp_config_disable_xxe; + +typedef struct { + HashTable *names; + uint32_t mask_ipv4; + uint32_t mask_ipv6; +} sp_config_cookie_encryption; + +typedef struct { + bool enable; + bool simulation; +} sp_config_unserialize; + +typedef struct { + char *filename; + pcre *r_filename; + + char *function; + pcre *r_function; + + char *hash; + int simulation; + bool enable; + + char *param; + pcre *r_param; + sp_php_type param_type; + + char *ret; + pcre *r_ret; + sp_php_type ret_type; + + pcre *regexp; + char *value; + + char *dump; + char *alias; + bool param_is_array; + bool var_is_array; + sp_node_t *param_array_keys; + sp_node_t *var_array_keys; + + bool allow; + bool drop; + + char *var; + + sp_cidr *cidr; +} sp_disabled_function; + +typedef struct { + sp_node_t *disabled_functions; // list of sp_disabled_function +} sp_config_disabled_functions; + +typedef struct { + sp_node_t *regexp_inclusion; // list of regexp for inclusion +} sp_config_regexp_inclusion; + +typedef struct { + char *script; + bool simulation; + bool enable; +} sp_config_upload_validation; + +typedef struct { + sp_config_random *config_random; + sp_config_unserialize *config_unserialize; + sp_config_disabled_functions *config_disabled_functions; + sp_config_disabled_functions *config_disabled_functions_ret; + sp_config_readonly_exec *config_readonly_exec; + sp_config_upload_validation *config_upload_validation; + sp_config_cookie_encryption *config_cookie_encryption; + sp_config_encryption_key *config_snuffleupagus; + sp_config_auto_cookie_secure *config_auto_cookie_secure; + sp_config_global_strict *config_global_strict; + sp_config_disable_xxe *config_disable_xxe; + sp_config_regexp_inclusion *config_regexp_inclusion; +} sp_config; + +typedef struct { + int (*func)(char *, char *, void *); + char *token; + void *retval; +} sp_config_functions; + +typedef struct { + int (*func)(char *); + char *token; +} sp_config_tokens; + +#define SP_TOKEN_BASE "sp" + +#define SP_TOKEN_AUTO_COOKIE_SECURE ".auto_cookie_secure" +#define SP_TOKEN_COOKIE_ENCRYPTION ".cookie_encryption" +#define SP_TOKEN_DISABLE_FUNC ".disable_functions" +#define SP_TOKEN_GLOBAL ".global" +#define SP_TOKEN_GLOBAL_STRICT ".global_strict" +#define SP_TOKEN_HARDEN_RANDOM ".harden_random" +#define SP_TOKEN_READONLY_EXEC ".readonly_exec" +#define SP_TOKEN_UNSERIALIZE_HMAC ".unserialize_hmac" +#define SP_TOKEN_UPLOAD_VALIDATION ".upload_validation" +#define SP_TOKEN_DISABLE_XXE ".disable_xxe" + +// common tokens +#define SP_TOKEN_ENABLE ".enable(" +#define SP_TOKEN_DISABLE ".disable(" +#define SP_TOKEN_SIMULATION ".simulation(" +#define SP_TOKEN_TRUE "1" +#define SP_TOKEN_FALSE "0" +#define SP_TOKEN_DUMP ".dump(" +#define SP_TOKEN_ALIAS ".alias(" +#define SP_TOKEN_ALLOW ".allow(" +#define SP_TOKEN_DROP ".drop(" + +#define SP_TOKEN_END_PARAM ')' + +// disable_function +#define SP_TOKEN_CIDR ".cidr(" +#define SP_TOKEN_FILENAME ".filename(" +#define SP_TOKEN_FILENAME_REGEXP ".filename_r(" +#define SP_TOKEN_FUNCTION ".function(" +#define SP_TOKEN_FUNCTION_REGEXP ".function_r(" +#define SP_TOKEN_HASH ".hash(" +#define SP_TOKEN_LOCAL_VAR ".var(" +#define SP_TOKEN_PARAM ".param(" +#define SP_TOKEN_PARAM_REGEXP ".param_r(" +#define SP_TOKEN_PARAM_TYPE ".param_type(" +#define SP_TOKEN_RET ".ret(" +#define SP_TOKEN_RET_REGEXP ".ret_r(" +#define SP_TOKEN_RET_TYPE ".ret_type(" +#define SP_TOKEN_VALUE ".value(" +#define SP_TOKEN_VALUE_REGEXP ".value_r(" + +// cookies encryption +#define SP_TOKEN_NAME ".cookie(" +#define SP_TOKEN_MASK_IPV4 ".mask_ipv4(" +#define SP_TOKEN_MASK_IPV6 ".mask_ipv6(" + +// Global configuration options +#define SP_TOKEN_ENCRYPTION_KEY ".secret_key(" + +// upload_validator +#define SP_TOKEN_UPLOAD_SCRIPT ".script(" + +int sp_parse_config(const char *); +int parse_array(sp_disabled_function *); + +int parse_str(char *restrict, char *restrict, void *); +int parse_regexp(char *restrict, char *restrict, void *); +int parse_empty(char *restrict, char *restrict, void *); +int parse_int(char *restrict, char *restrict, void *); +int parse_cidr(char *restrict, char *restrict, void *); +int parse_php_type(char *restrict, char *restrict, void *); + + +#endif /* SP_CONFIG_H */ diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c new file mode 100644 index 00000000..4a6dd3ad --- /dev/null +++ b/src/sp_config_keywords.c @@ -0,0 +1,268 @@ +#include "php_snuffleupagus.h" + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) + +static int parse_enable(char *line, bool * restrict retval, bool * restrict simulation) { + bool enable = false, disable = false; + sp_config_functions sp_config_funcs[] = { + {parse_empty, SP_TOKEN_ENABLE, &(enable)}, + {parse_empty, SP_TOKEN_DISABLE, &(disable)}, + {parse_empty, SP_TOKEN_SIMULATION, simulation}, + {0}}; + + int ret = parse_keywords(sp_config_funcs, line); + + if (0 != ret) { + return ret; + } + + if (!(enable ^ disable)) { + sp_log_err("config", "A rule can't be enabled and disabled."); + return -1; + } + + *retval = enable; + + return ret; +} + +int parse_random(char *line) { + return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_random->enable), NULL); +} + +int parse_disable_xxe(char *line) { + return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_disable_xxe->enable), NULL); +} + +int parse_auto_cookie_secure(char *line) { + return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_auto_cookie_secure->enable), NULL); +} + +int parse_global_strict(char *line) { + return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_global_strict->enable), NULL); +} + +int parse_unserialize(char *line) { + return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_unserialize->enable), &(SNUFFLEUPAGUS_G(config).config_unserialize->simulation)); +} + +int parse_readonly_exec(char *line) { + return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_readonly_exec->enable), &(SNUFFLEUPAGUS_G(config).config_readonly_exec->simulation)); +} + +int parse_global(char *line) { + sp_config_functions sp_config_funcs_encryption_key[] = { + {parse_str, SP_TOKEN_ENCRYPTION_KEY, + &(SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key)}, + {0}}; + return parse_keywords(sp_config_funcs_encryption_key, line); +} + +int parse_cookie_encryption(char *line) { + int ret = 0; + char *name = NULL; + + sp_config_functions sp_config_funcs_cookie_encryption[] = { + {parse_str, SP_TOKEN_NAME, &name}, + {parse_int, SP_TOKEN_MASK_IPV4, + &(SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv4)}, + {parse_int, SP_TOKEN_MASK_IPV6, + &(SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv6)}, + {0}}; + + ret = parse_keywords(sp_config_funcs_cookie_encryption, line); + if (0 != ret) { + return ret; + } + + if (32 < SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv4) { + SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv4 = 32; + } + if (128 < SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv6) { + SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv6 = 128; + } + + if (name) { + zend_hash_str_add_empty_element( + SNUFFLEUPAGUS_G(config).config_cookie_encryption->names, name, + strlen(name)); + } + return SUCCESS; +} + +int parse_disabled_functions(char *line) { + int ret = 0; + bool enable = true, disable = false; + sp_disabled_function *df = pecalloc(sizeof(*df), 1, 1); + + sp_config_functions sp_config_funcs_disabled_functions[] = { + {parse_empty, SP_TOKEN_ENABLE, &(enable)}, + {parse_empty, SP_TOKEN_DISABLE, &(disable)}, + {parse_str, SP_TOKEN_ALIAS, &(df->alias)}, + {parse_empty, SP_TOKEN_SIMULATION, &(df->simulation)}, + {parse_str, SP_TOKEN_FILENAME, &(df->filename)}, + {parse_regexp, SP_TOKEN_FILENAME_REGEXP, &(df->r_filename)}, + {parse_str, SP_TOKEN_FUNCTION, &(df->function)}, + {parse_regexp, SP_TOKEN_FUNCTION_REGEXP, &(df->r_function)}, + {parse_str, SP_TOKEN_DUMP, &(df->dump)}, + {parse_empty, SP_TOKEN_ALLOW, &(df->allow)}, + {parse_empty, SP_TOKEN_DROP, &(df->drop)}, + {parse_str, SP_TOKEN_HASH, &(df->hash)}, + {parse_str, SP_TOKEN_PARAM, &(df->param)}, + {parse_regexp, SP_TOKEN_VALUE_REGEXP, &(df->regexp)}, + {parse_str, SP_TOKEN_VALUE, &(df->value)}, + {parse_regexp, SP_TOKEN_PARAM_REGEXP, &(df->r_param)}, + {parse_php_type, SP_TOKEN_PARAM_TYPE, &(df->param_type)}, + {parse_str, SP_TOKEN_RET, &(df->ret)}, + {parse_cidr, SP_TOKEN_CIDR, &(df->cidr)}, + {parse_regexp, SP_TOKEN_RET_REGEXP, &(df->r_ret)}, + {parse_php_type, SP_TOKEN_RET_TYPE, &(df->ret_type)}, + {parse_str, SP_TOKEN_LOCAL_VAR, &(df->var)}, + {0}}; + + ret = parse_keywords(sp_config_funcs_disabled_functions, line); + + if (0 != ret) { + return ret; + } + + if (true == disable){ + df->enable = false; + } else { + df->enable = true; + } + + if (df->value && df->regexp) { + sp_log_err("config", + "Invalid configuration line: 'sp.disabled_functions%s':" + "'.value' and '.regexp' are mutually exclusives.", + line); + return -1; + } else if (df->r_function && df->function) { + sp_log_err("config", + "Invalid configuration line: 'sp.disabled_functions%s': " + "'.r_function' and '.function' are mutually exclusive.", + line); + return -1; + } else if (df->r_filename && df->filename) { + sp_log_err("config", + "Invalid configuration line: 'sp.disabled_functions%s':" + "'.r_filename' and '.filename' are mutually exclusive.", + line); + return -1; + } else if (df->r_param && df->param) { + sp_log_err("config", + "Invalid configuration line: 'sp.disabled_functions%s':" + "'.r_param' and '.param' are mutually exclusive.", + line); + return -1; + } else if (df->r_ret && df->ret) { + sp_log_err("config", + "Invalid configuration line: 'sp.disabled_functions%s':" + "'.r_ret' and '.ret' are mutually exclusive.", + line); + return -1; + } else if ((df->r_ret || df->ret) && (df->r_param || df->param)) { + sp_log_err("config", + "Invalid configuration line: 'sp.disabled_functions%s':" + "`ret` and `param` are mutually exclusives.", + line); + return -1; + } else if (!(df->r_function || df->function)) { + sp_log_err("config", + "Invalid configuration line: 'sp.disabled_functions%s':" + " must take a function name.", + line); + return -1; + } else if (!(df->allow ^ df->drop)) { + sp_log_err("config", + "Invalid configuration line: 'sp.disabled_functions%s': The " + "rule must either be a `drop` or and `allow` one.", + line); + return -1; + } + + if (df->param && strchr(df->param, '[')) { // assume that this is an array + df->param_array_keys = sp_new_list(); + if (0 != array_to_list(&df->param, &df->param_array_keys)) { + pefree(df->param_array_keys, 1); + return -1; + } + df->param_is_array = 1; + } + + if (df->var && strchr(df->var, '[')) { // assume that this is an array + df->var_array_keys = sp_new_list(); + if (0 != array_to_list(&df->var, &df->var_array_keys)) { + pefree(df->var_array_keys, 1); + return -1; + } + df->var_is_array = 1; + } + + bool match = false; + const char *key[4] = {"include", "include_once", "require", "require_once"}; + for (size_t i = 0; i < 4; i++) { + if (df->r_function && true == is_regexp_matching(df->r_function, key[i])) { + match = true; + break; + } else if (df->function && 0 == strcmp(df->function, key[i])) { + match = true; + break; + } + } + if (true == match && df->regexp) { + sp_list_insert( + SNUFFLEUPAGUS_G(config).config_regexp_inclusion->regexp_inclusion, + df->regexp); + } else if (df->ret || df->r_ret || df->ret_type) { + sp_list_insert( + SNUFFLEUPAGUS_G(config).config_disabled_functions_ret->disabled_functions, + df); + } else { + sp_list_insert( + SNUFFLEUPAGUS_G(config).config_disabled_functions->disabled_functions, + df); + } + return ret; +} + +int parse_upload_validation(char *line) { + bool disable = false, enable = false; + sp_config_functions sp_config_funcs_upload_validation[] = { + {parse_str, SP_TOKEN_UPLOAD_SCRIPT, + &(SNUFFLEUPAGUS_G(config).config_upload_validation->script)}, + {parse_empty, SP_TOKEN_SIMULATION, + &(SNUFFLEUPAGUS_G(config).config_upload_validation->simulation)}, + {parse_empty, SP_TOKEN_ENABLE, &(enable)}, + {parse_empty, SP_TOKEN_DISABLE, &(disable)}, + {0}}; + + int ret = parse_keywords(sp_config_funcs_upload_validation, line); + + if (0 != ret) { + return ret; + } + + if (!(enable ^ disable)) { + sp_log_err("config", "A rule can't be enabled and disabled."); + return -1; + } + SNUFFLEUPAGUS_G(config).config_upload_validation->enable = enable; + + char const *script = SNUFFLEUPAGUS_G(config).config_upload_validation->script; + + if (!script) { + sp_log_err("config", "The `script` directive is mandatory in %s", + line); + return -1; + } else if (-1 == access(script, F_OK)) { + sp_log_err("config", "The `script` (%s) doesn't exist.", script); + return -1; + } else if (-1 == access(script, X_OK)) { + sp_log_err("config", "The `script` (%s) isn't executable.", script); + return -1; + } + + return ret; +} diff --git a/src/sp_config_keywords.h b/src/sp_config_keywords.h new file mode 100644 index 00000000..40fac47e --- /dev/null +++ b/src/sp_config_keywords.h @@ -0,0 +1,16 @@ +#ifndef SP_CONFIG_KEYWORDS_H +#define SP_CONFIG_KEYWORDS_H +#include "php_snuffleupagus.h" + +int parse_random(char *line); +int parse_disable_xxe(char *line); +int parse_auto_cookie_secure(char *line); +int parse_global_strict(char *line); +int parse_global(char *line) ; +int parse_cookie_encryption(char *line); +int parse_unserialize(char *line) ; +int parse_readonly_exec(char *line); +int parse_disabled_functions(char *line) ; +int parse_upload_validation(char *line); + +#endif // __SP_CONFIG_KEYWORDS_H \ No newline at end of file diff --git a/src/sp_config_utils.c b/src/sp_config_utils.c new file mode 100644 index 00000000..e05e95e0 --- /dev/null +++ b/src/sp_config_utils.c @@ -0,0 +1,211 @@ +#include "php_snuffleupagus.h" + +static int validate_int(const char *value); +static int validate_str(const char *value); + +static sp_pure int validate_int(const char *value) { + for (size_t i = 0; i < strlen(value); i++) { + if (!isdigit(value[i])) { + return -1; + } + } + return 0; +} + +static sp_pure int validate_str(const char *value) { + int balance = 0; // ghetto [] validation + + if (!strchr(value, '[')) { + return 0; + } + + for (size_t i = 0; i < strlen(value); i++) { + if (value[i] == '[') { + balance++; + } else if (value[i] == ']') { + balance--; + } + if (balance < 0) { + return -1; + } + } + return balance != 0; +} + +int parse_keywords(sp_config_functions *funcs, char *line) { + int value_len = 0; + const char *original_line = line; + for (size_t i = 0; funcs[i].func; i++) { + if (!strncmp(funcs[i].token, line, strlen(funcs[i].token))) { + line += strlen(funcs[i].token); + value_len = funcs[i].func(line, funcs[i].token, funcs[i].retval) + 1; + if (value_len == 0) { // bad parameter + return -1; + } + line += value_len; + i = -1; // we start the loop again + } + } + while (*line == ';' || *line == '\t' || *line == ' ') { + line++; + } + + if (*line == '#') { + return 0; + } + + if (*line) { + sp_log_err("config", "Trailing chars '%s' at the end of '%s'.", line, + original_line); + return -1; + } + return 0; +} + +static char *get_string(size_t *consumed, char *restrict line, + const char *restrict keyword) { + enum { IN_ESCAPE, NONE } state = NONE; + char *original_line = line; + size_t j = 0; + + char *ret = NULL; + if (NULL == line) { + goto err; + } + + ret = pecalloc(sizeof(char), strlen(original_line) + 1, 1); + + /* The first char of a string is always '"', since they MUST be quoted. */ + if ('"' == *line) { + line++; + } else { + goto err; + } + + for (size_t i = 0; line[i] && j < strlen(original_line) - 2; i++) { + switch (line[i]) { + case '"': + /* A double quote at this point is either: + - at the very end of the string. + - escaped + */ + if ((state == NONE) && (line[i + 1] == SP_TOKEN_END_PARAM)) { + /* The `+2` if for + 1. the terminal double-quote + 2. the SP_TOKEN_END_PARAM + */ + *consumed = i + 2; + return ret; + } else if (state == IN_ESCAPE) { + break; // we're on an escped double quote + } else { + goto err; + } + case '\\': + if (state == NONE) { + state = IN_ESCAPE; + continue; + } + default: + break; + } + if (state == IN_ESCAPE) { + state = NONE; + } + ret[j++] = line[i]; + } +err: + sp_log_err("error", + "There is an issue with the parsing of '%s': it doesn't look like a valid string.", + original_line ? original_line : "NULL"); + line = NULL; + return NULL; +} + +static char *get_misc(char *restrict line, const char *restrict keyword) { + size_t i = 0; + char *ret = pecalloc(sizeof(char), 1024, 1); + + while (i < 1024 - 1 && line[i] && line[i] != SP_TOKEN_END_PARAM) { + ret[i] = line[i]; + i++; + } + + if (line[i] != SP_TOKEN_END_PARAM) { + if (i >= 1024 - 1) { + sp_log_err("config", "The following line is too long: %s.", line); + } else { + sp_log_err("config", "Missing closing %c in line %s.", SP_TOKEN_END_PARAM, + line); + } + return NULL; + } else if (i == 0) { + sp_log_err("config", "The keyword %s%c is expecting a parameter.", + keyword, SP_TOKEN_END_PARAM); + return NULL; + } + return ret; +} + +char *get_param(size_t *consumed, char *restrict line, sp_type type, + const char *restrict keyword) { + char *retval = NULL; + if (type == SP_TYPE_STR) { + retval = get_string(consumed, line, keyword); + } else { + retval = get_misc(line, keyword); + *consumed = retval ? strlen(retval) : 0; + } + + if (retval) { + if (type == SP_TYPE_STR && 0 == validate_str(retval)) { + return retval; + } else if (type == SP_TYPE_INT && 0 == validate_int(retval)) { + return retval; + } + } + return NULL; +} + +// FIXME this is leaking like hell @blotus +int array_to_list(char **name_ptr, sp_node_t **keys) { + int in_key = 0; + size_t i = 0; + char *name = *name_ptr; + char *key_name = ecalloc(strlen(name) + 1, 1); // im way too lazy for + // now + char *tmp = ecalloc(strlen(name) + 1, 1); + + for (i = 0; name[i] != '['; i++) { + tmp[i] = name[i]; + } + tmp[i] = 0; + + for (size_t j = 0; name[i]; i++) { + const char c = name[i]; + if (c == '[') { + if (in_key == 0) { + in_key = 1; + } else { + efree(key_name); + return -1; + } + } else if (c == ']') { + if (in_key == 0) { + efree(key_name); + return -1; + } else { + in_key = 0; + j = 0; + sp_list_insert(*keys, pestrdup(key_name, 1)); + memset(key_name, 0, strlen(name) + 1); + } + } else if (in_key == 1) { + key_name[j] = c; + j++; + } + } + efree(key_name); + *name_ptr = pestrdup(tmp, 1); + return in_key; +} diff --git a/src/sp_config_utils.h b/src/sp_config_utils.h new file mode 100644 index 00000000..f2f8fce2 --- /dev/null +++ b/src/sp_config_utils.h @@ -0,0 +1,8 @@ +#ifndef SP_CONFIG_UTILS +#define SP_CONFIG_UTILS + +int parse_keywords(sp_config_functions *, char *); +char *get_param(size_t *, char *restrict, sp_type, const char *restrict); +int array_to_list(char **, sp_node_t **); + +#endif /* SP_CONFIG_UTILS */ diff --git a/src/sp_cookie_encryption.c b/src/sp_cookie_encryption.c new file mode 100644 index 00000000..5248486f --- /dev/null +++ b/src/sp_cookie_encryption.c @@ -0,0 +1,216 @@ +#include "php_snuffleupagus.h" + +#include "ext/standard/url.h" + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) + +static unsigned int nonce_d = 0; + +static inline void generate_key(unsigned char *key) { + PHP_SHA256_CTX ctx; + const char *user_agent = sp_getenv("HTTP_USER_AGENT"); + const char *remote_addr = sp_getenv("REMOTE_ADDR"); + const char *encryption_key = + SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key; + + /* 32 is the size of a SHA256. */ + assert(32 == crypto_secretbox_KEYBYTES); + + PHP_SHA256Init(&ctx); + + if (user_agent) { + PHP_SHA256Update(&ctx, (unsigned char *)user_agent, strlen(user_agent)); + } + + if (remote_addr) { + char out[128]; + apply_mask_on_ip(out, remote_addr); + PHP_SHA256Update(&ctx, (unsigned char*)out, sizeof(out)); + } + + if (encryption_key) { + PHP_SHA256Update(&ctx, (const unsigned char *)encryption_key, + strlen(encryption_key)); + } + + PHP_SHA256Final((unsigned char *)key, &ctx); +} + +int decrypt_cookie(zval *pDest, int num_args, va_list args, + zend_hash_key *hash_key) { + unsigned char key[crypto_secretbox_KEYBYTES] = {0}; + size_t value_len; + zend_string *debase64; + unsigned char *decrypted; + int ret = 0; + + /* If the cookie isn't in the conf, it shouldn't be encrypted. */ + if (0 == + zend_hash_exists(SNUFFLEUPAGUS_G(config).config_cookie_encryption->names, + hash_key->key)) { + return ZEND_HASH_APPLY_KEEP; + } + + generate_key(key); + + value_len = php_url_decode(Z_STRVAL_P(pDest), Z_STRLEN_P(pDest)); + + if (value_len == 0) { + return ZEND_HASH_APPLY_KEEP; + } + + debase64 = php_base64_decode((unsigned char *)(Z_STRVAL_P(pDest)), value_len); + + if (value_len < + crypto_secretbox_NONCEBYTES + crypto_secretbox_ZEROBYTES) { + sp_log_msg("cookie_encryption", LOG_DROP, + "Buffer underflow tentative detected in cookie encryption handling."); + return ZEND_HASH_APPLY_REMOVE; + } + + decrypted = pecalloc(value_len, 1, 0); + + ret = crypto_secretbox_open( + decrypted, + (unsigned char *)(ZSTR_VAL(debase64) + crypto_secretbox_NONCEBYTES), + ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES, + (unsigned char *)ZSTR_VAL(debase64), key); + + if (ret == -1) { + sp_log_msg("cookie_encryption", LOG_DROP, + "Something went wrong with the decryption of %s.", + ZSTR_VAL(hash_key->key)); + return ZEND_HASH_APPLY_REMOVE; + } + + ZVAL_STRINGL(pDest, (char *)(decrypted + crypto_secretbox_ZEROBYTES), + ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES - 1 - + crypto_secretbox_ZEROBYTES); + + return ZEND_HASH_APPLY_KEEP; +} + +/** + This function will return the `data` of length `data_len` encrypted in the + form + base64(nonce | encrypted_data) (with `|` being the concatenation + operation). + + The `nonce` is time-based. +*/ +static zend_string *encrypt_data(char *data, unsigned long long data_len) { + const size_t encrypted_msg_len = crypto_secretbox_ZEROBYTES + data_len + 1; + const size_t emsg_and_nonce_len = encrypted_msg_len + crypto_secretbox_NONCEBYTES; + + unsigned char key[crypto_secretbox_KEYBYTES] = {0}; + unsigned char nonce[crypto_secretbox_NONCEBYTES] = {0}; + unsigned char *data_to_encrypt = pecalloc(encrypted_msg_len, 1, 0); + unsigned char *encrypted_data = pecalloc(emsg_and_nonce_len, 1, 1); + + generate_key(key); + + /* tweetnacl's API requires the message to be padded with + crypto_secretbox_ZEROBYTES zeroes. */ + memcpy(data_to_encrypt + crypto_secretbox_ZEROBYTES, data, data_len); + + assert(sizeof(size_t) <= crypto_secretbox_NONCEBYTES); + + nonce_d++; + sscanf((char*)nonce, "%ud", &nonce_d); + + memcpy(encrypted_data, nonce, crypto_secretbox_NONCEBYTES); + crypto_secretbox(encrypted_data + crypto_secretbox_NONCEBYTES, + data_to_encrypt, encrypted_msg_len, nonce, key); + + zend_string *z = php_base64_encode(encrypted_data, emsg_and_nonce_len); + sp_log_debug("cookie_encryption", "Cookie value:%s:", z->val); + return z; +} + +PHP_FUNCTION(sp_setcookie) { + zval params[7] = { 0 }; + zend_string *name = NULL, *value = NULL, *path = NULL, *domain = NULL; + zend_long expires = 0; + zend_bool secure = 0, httponly = 0; + zval ret_val; + zval func_name; + + ZEND_PARSE_PARAMETERS_START(1, 7) + Z_PARAM_STR(name) + Z_PARAM_OPTIONAL + Z_PARAM_STR(value) + Z_PARAM_LONG(expires) + Z_PARAM_STR(path) + Z_PARAM_STR(domain) + Z_PARAM_BOOL(secure) + Z_PARAM_BOOL(httponly) + ZEND_PARSE_PARAMETERS_END(); + + /* If the request was issued over HTTPS, the cookie should be "secure" */ + if (SNUFFLEUPAGUS_G(config).config_auto_cookie_secure) { + const zval server_vars = PG(http_globals)[TRACK_VARS_SERVER]; + if (Z_TYPE(server_vars) == IS_ARRAY) { + const zval *is_https = + zend_hash_str_find(Z_ARRVAL(server_vars), "HTTPS", strlen("HTTPS")); + if (NULL != is_https) { + secure = 1; + } + } + } + + /* If the cookie's value is encrypted, it won't be usable by + * javascript anyway. + */ + if (zend_hash_exists(SNUFFLEUPAGUS_G(config).config_cookie_encryption->names, + name) > 0) { + httponly = 1; + } + + /* Shall we encrypt the cookie's value? */ + if (zend_hash_exists(SNUFFLEUPAGUS_G(config).config_cookie_encryption->names, + name) > 0 && value) { + zend_string *encrypted_data = encrypt_data(value->val, value->len); + ZVAL_STR_COPY(¶ms[1], encrypted_data); + zend_string_release(encrypted_data); + } else if (value) { + ZVAL_STR_COPY(¶ms[1], value); + } + + ZVAL_STRING(&func_name, "setcookie"); + ZVAL_STR_COPY(¶ms[0], name); + ZVAL_LONG(¶ms[2], expires); + if (path) { + ZVAL_STR_COPY(¶ms[3], path); + } + if (domain) { + ZVAL_STR_COPY(¶ms[4], domain); + } + if (secure) { + ZVAL_LONG(¶ms[5], secure); + } + if (httponly) { + ZVAL_LONG(¶ms[6], httponly); + } + + /* This is the _fun_ part: because PHP is utterly idiotic and nonsensical, + the `call_user_function` macro will __discard__ (yes) its first argument + (the hashtable), effectively calling functions from `CG(function_table)`. + This is why were replacing our hook with the original function, calling + the function, and then re-hooking it. */ + void (*handler)(INTERNAL_FUNCTION_PARAMETERS); + handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook), "setcookie", + strlen("setcookie")); + zend_internal_function *func = zend_hash_str_find_ptr( + CG(function_table), "setcookie", strlen("setcookie")); + func->handler = handler; + + call_user_function(CG(function_table), NULL, &func_name, &ret_val, 7, params); + + func->handler = PHP_FN(sp_setcookie); +} + +int hook_cookies() { + HOOK_FUNCTION("setcookie", sp_internal_functions_hook, PHP_FN(sp_setcookie), false); + + return SUCCESS; +} diff --git a/src/sp_cookie_encryption.h b/src/sp_cookie_encryption.h new file mode 100644 index 00000000..9904738d --- /dev/null +++ b/src/sp_cookie_encryption.h @@ -0,0 +1,17 @@ + +#ifndef __SP_COOKIE_ENCRYPTION +#define __SP_COOKIE_ENCRYPTION + +#include "SAPI.h" +#include "tweetnacl.h" + +#include "sp_utils.h" + +#include "ext/hash/php_hash.h" +#include "ext/hash/php_hash_sha.h" +#include "ext/standard/base64.h" + +int hook_cookies(); +int decrypt_cookie(zval *pDest, int num_args, va_list args, zend_hash_key *hash_key); + +#endif /* __SP_COOKIE_ENCRYPTION */ diff --git a/src/sp_disable_xxe.c b/src/sp_disable_xxe.c new file mode 100644 index 00000000..d11b3d03 --- /dev/null +++ b/src/sp_disable_xxe.c @@ -0,0 +1,25 @@ +#include "php_snuffleupagus.h" + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) + +PHP_FUNCTION(sp_libxml_disable_entity_loader) { RETURN_TRUE; } + +int hook_libxml_disable_entity_loader() { + zval func_name; + zval hmac; + zval params[1]; + + TSRMLS_FETCH(); + + /* Call the php function here instead of re-implementing it is a bit + * ugly, but we do not want to introduce compile-time dependencies against + * libxml. */ + ZVAL_STRING(&func_name, "libxml_disable_entity_loader"); + ZVAL_STRING(¶ms[0], "true"); + call_user_function(CG(function_table), NULL, &func_name, &hmac, 1, params); + + HOOK_FUNCTION("libxml_disable_entity_loader", sp_internal_functions_hook, + PHP_FN(sp_libxml_disable_entity_loader), false); + + return SUCCESS; +} diff --git a/src/sp_disable_xxe.h b/src/sp_disable_xxe.h new file mode 100644 index 00000000..274c169b --- /dev/null +++ b/src/sp_disable_xxe.h @@ -0,0 +1,6 @@ +#ifndef __SP_DISABLE_XXE_H +#define __SP_DISABLE_XXE_H + +int hook_libxml_disable_entity_loader(); + +#endif /* __SP_DISABLE_XXE_H */ diff --git a/src/sp_disabled_functions.c b/src/sp_disabled_functions.c new file mode 100644 index 00000000..55d782b5 --- /dev/null +++ b/src/sp_disabled_functions.c @@ -0,0 +1,356 @@ +#include "php_snuffleupagus.h" + +#include "zend_execute.h" +#include "zend_hash.h" + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus); + +ZEND_COLD static zend_always_inline bool is_hash_matching( + const char* current_filename, + sp_disabled_function const* const config_node) { + char current_file_hash[SHA256_SIZE * 2]; + compute_hash(current_filename, current_file_hash); + return (0 == strncmp(current_file_hash, config_node->hash, SHA256_SIZE)); +} + +static zend_always_inline char* get_complete_function_path( + zend_execute_data const* const execute_data) { + char const* class_name; + char const* const function_name = + ZSTR_VAL(execute_data->func->common.function_name); + char* complete_path_function = NULL; + + class_name = get_active_class_name(NULL); + if (*class_name) { + const size_t len = strlen(class_name) + 2 + strlen(function_name) + 1; + complete_path_function = emalloc(len); + snprintf(complete_path_function, len, "%s::%s", class_name, function_name); + } else { + complete_path_function = estrdup(function_name); + } + return complete_path_function; +} + +static bool is_local_var_matching(zend_execute_data *execute_data, const sp_disabled_function *const config_node) { + zend_execute_data *orig_execute_data = execute_data; + + /*because execute_data points to hooked function data, + which we dont care about */ + zend_execute_data *current = execute_data->prev_execute_data; + zval *value = NULL; + + while (current) { + zend_string *key = NULL; + EG(current_execute_data) = current; + zend_array *symtable = zend_rebuild_symbol_table(); + ZEND_HASH_FOREACH_STR_KEY_VAL(symtable, key, value) { + if (0 == strcmp(config_node->var, key->val)) { // is the var name right? + if (Z_TYPE_P(value) == IS_INDIRECT) { + value = Z_INDIRECT_P(value); + } + if (Z_TYPE_P(value) != IS_ARRAY) { + char *var_value_str = sp_convert_to_string(value); + if (true == sp_match_value(var_value_str, config_node->value, config_node->regexp)) { + efree(var_value_str); + EG(current_execute_data) = orig_execute_data; + return true; + } + efree(var_value_str); + } + else { + EG(current_execute_data) = orig_execute_data; + return sp_match_array_key_recurse(value, config_node->var_array_keys, config_node->value, NULL); + } + } + } + ZEND_HASH_FOREACH_END(); + current = current->prev_execute_data; + } + + EG(current_execute_data) = orig_execute_data; + return false; +} + +bool should_disable(zend_execute_data* execute_data) { + const char* current_filename = zend_get_executed_filename(TSRMLS_C); + const sp_node_t* config = + SNUFFLEUPAGUS_G(config).config_disabled_functions->disabled_functions; + const char* function_name = + ZSTR_VAL(execute_data->func->common.function_name); + char* complete_path_function; + char const* client_ip = sp_getenv("REMOTE_ADDR"); + + if (!function_name) { + return false; + } + + if (!config || !config->data) { + return false; + } + + complete_path_function = get_complete_function_path(execute_data); + + while (config) { + sp_disabled_function const* const config_node = + (sp_disabled_function*)(config->data); + const char* arg_name = NULL; + const char* arg_value_str = NULL; + + if (false == config_node->enable) { + goto next; + } + + if (config_node->function) { /* Litteral match against the function name. */ + if (0 != strcmp(config_node->function, complete_path_function)) { + goto next; + } + } else if (config_node->r_function) { + if (false == + is_regexp_matching(config_node->r_function, complete_path_function)) { + goto next; + } + } + if (config_node->var) { + if (false == is_local_var_matching(execute_data, config_node)) { + goto next; + } + } + + if (config_node->filename) { /* Check the current file name. */ + if (0 != strcmp(current_filename, config_node->filename)) { + goto next; + } + } else if (config_node->r_filename) { + if (false == + is_regexp_matching(config_node->r_filename, current_filename)) { + goto next; + } + } + + if (config_node->hash) { + if (false == is_hash_matching(current_filename, config_node)) { + goto next; + } + } + + if (client_ip && config_node->cidr && + (false == cidr_match(client_ip, config_node->cidr))) { + goto next; + } + + /* Check if we filter on parameter value*/ + if (config_node->param || config_node->r_param) { + const unsigned int nb_param = execute_data->func->common.num_args; + bool arg_matched = false; + + for (unsigned int i = 0; i < nb_param; i++) { + arg_matched = false; + if (ZEND_USER_CODE(execute_data->func->type)) { // yay consistency + arg_name = ZSTR_VAL(execute_data->func->common.arg_info[i].name); + } else { + arg_name = execute_data->func->internal_function.arg_info[i].name; + } + + const bool arg_matching = + config_node->param && (0 == strcmp(arg_name, config_node->param)); + const bool pcre_matching = + config_node->r_param && + (true == is_regexp_matching(config_node->r_param, arg_name)); + + /* This is the parameter name we're looking for. */ + if (true == arg_matching || true == pcre_matching) { + zval* arg_value = ZEND_CALL_VAR_NUM(execute_data, i); + + if (config_node->param_type) { // Are we matching on the `type`? + if (config_node->param_type == Z_TYPE_P(arg_value)) { + arg_matched = true; + break; + } + } else if (Z_TYPE_P(arg_value) == IS_ARRAY) { + arg_value_str = estrdup("Array"); + // match on arr -> match on all key content, if a key is an array, + // ignore it + // match on arr[foo] -> match only on key foo, if the key is an + // array, match on all keys content + if (config_node->param_is_array == true) { + if (true == sp_match_array_key_recurse( + arg_value, config_node->param_array_keys, + config_node->value, config_node->regexp)) { + arg_matched = true; + break; + } + } else { // match on all keys, but don't go into subarray + if (true == sp_match_array_key(arg_value, config_node->value, + config_node->regexp)) { + arg_matched = true; + break; + } + } + } else { + arg_value_str = sp_convert_to_string(arg_value); + if (true == sp_match_value(arg_value_str, config_node->value, + config_node->regexp)) { + arg_matched = true; + break; + } + } + } + } + if (false == arg_matched) { + goto next; + } + } + + /* Everything matched.*/ + + if (true == config_node->allow) { + goto allow; + } + + sp_log_disable(complete_path_function, arg_name, arg_value_str, + config_node); + if (true == config_node->simulation) { + goto next; + } else { // We've got a match, the function won't be executed + efree(complete_path_function); + return true; + } +next: +config = config->next; + } +allow: + efree(complete_path_function); + return false; +} + +static bool should_drop_on_ret(zval* return_value, + const zend_execute_data* const execute_data) { + const sp_node_t* config = + SNUFFLEUPAGUS_G(config).config_disabled_functions_ret->disabled_functions; + char* complete_path_function = get_complete_function_path(execute_data); + const char* current_filename = zend_get_executed_filename(TSRMLS_C); + + if (!config || !config->data) { + return false; + } + + while (config) { + char* ret_value_str = NULL; + sp_disabled_function const* const config_node = + (sp_disabled_function*)(config->data); + + if (false == config_node->enable) { + goto next; + } + + if (config_node->function) { + if (0 != strcmp(config_node->function, complete_path_function)) { + goto next; + } + } else if (config_node->r_function) { + if (false == + is_regexp_matching(config_node->r_function, complete_path_function)) { + goto next; + } + } + + if (config_node->filename) { /* Check the current file name. */ + if (0 != strcmp(current_filename, config_node->filename)) { + goto next; + } + } else if (config_node->r_filename) { + if (false == + is_regexp_matching(config_node->r_filename, current_filename)) { + goto next; + } + } + + if (config_node->hash) { + if (false == is_hash_matching(current_filename, config_node)) { + goto next; + } + } + + ret_value_str = sp_convert_to_string(return_value); // FIXME memleak + + bool match_type = (config_node->ret_type) && + (config_node->ret_type == Z_TYPE_P(return_value)); + bool match_value = (config_node->ret || config_node->r_ret) && + (true == sp_match_value(ret_value_str, config_node->ret, + config_node->r_ret)); + + if (true == match_type || match_value) { + if (true == config_node->allow) { + efree(complete_path_function); + return false; + } + sp_log_disable_ret(complete_path_function, ret_value_str, config_node); + if (false == config_node->simulation) { + efree(complete_path_function); + return true; + } + } + next: + config = config->next; + } + efree(complete_path_function); + return false; +} + +ZEND_FUNCTION(check_disabled_function) { + void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS); + const char* current_function_name = get_active_function_name(TSRMLS_C); + + if (true == should_disable(execute_data)) { + return; + } + + if ((orig_handler = zend_hash_str_find_ptr( + SNUFFLEUPAGUS_G(disabled_functions_hook), current_function_name, + strlen(current_function_name)))) { + orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + if (true == should_drop_on_ret(return_value, execute_data)) { + zend_bailout(); + } + } else { + sp_log_err( + "disabled_functions", + "Unable to find the pointer to the original function '%s' in the " + "hashtable.\n", + current_function_name); + } +} + +static int hook_functions(const sp_node_t* config) { + while (config && config->data) { + const char* function_name = ((sp_disabled_function*)config->data)->function; + const pcre* function_name_regexp = + ((sp_disabled_function*)config->data)->r_function; + + if (NULL != function_name) { // hook function by name + HOOK_FUNCTION(function_name, disabled_functions_hook, + PHP_FN(check_disabled_function), false); + } else if (NULL != function_name_regexp) { // hook function by regexp + HOOK_FUNCTION_BY_REGEXP(function_name_regexp, disabled_functions_hook, + PHP_FN(check_disabled_function), false); + } else { + return FAILURE; + } + + config = config->next; + } + return SUCCESS; +} + +int hook_disabled_functions(void) { + TSRMLS_FETCH(); + + int ret = SUCCESS; + + ret |= hook_functions( + SNUFFLEUPAGUS_G(config).config_disabled_functions->disabled_functions); + ret |= hook_functions(SNUFFLEUPAGUS_G(config) + .config_disabled_functions_ret->disabled_functions); + + return ret; +} diff --git a/src/sp_disabled_functions.h b/src/sp_disabled_functions.h new file mode 100644 index 00000000..680eabb4 --- /dev/null +++ b/src/sp_disabled_functions.h @@ -0,0 +1,11 @@ +#ifndef __SP_DISABLE_FUNCTIONS_H +#define __SP_DISABLE_FUNCTIONS_H + +#include "ext/hash/php_hash.h" +#include "ext/hash/php_hash_sha.h" +#include "ext/standard/md5.h" + +int hook_disabled_functions(); +bool should_disable(zend_execute_data* function_name); + +#endif /* __SP_DISABLE_FUNCTIONS_H */ diff --git a/src/sp_execute.c b/src/sp_execute.c new file mode 100644 index 00000000..faf126c8 --- /dev/null +++ b/src/sp_execute.c @@ -0,0 +1,100 @@ +#include "php_snuffleupagus.h" + +#include +#include + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus); + +static void (*orig_execute_ex)(zend_execute_data *execute_data); +static int (*orig_zend_stream_open)(const char *filename, + zend_file_handle *handle); + +// FIXME handle symlink +ZEND_COLD static inline void terminate_if_writable(const char *filename) { + if (0 == access(filename, W_OK)) { + if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->simulation) { + sp_log_msg("readonly_exec", LOG_NOTICE, + "Attempted execution of a writable file (%s).", filename); + } else { + sp_log_msg("readonly_exec", LOG_DROP, + "Attempted execution of a writable file (%s).", filename); + sp_terminate(); + } + } else { + if (EACCES != errno) { + sp_log_err("Writable execution", "Error while accessing %s: %s", filename, + strerror(errno)); + } + } +} + +static void check_inclusion_regexp(const char * const filename) { + if (SNUFFLEUPAGUS_G(config).config_regexp_inclusion->regexp_inclusion) { + const sp_node_t* config = SNUFFLEUPAGUS_G(config).config_regexp_inclusion->regexp_inclusion; + if (!config || !config->data) { + return; + } + while (config) { + pcre *config_node = (pcre*)(config->data); + if (false == is_regexp_matching(config_node, filename)) { + sp_log_msg("include", LOG_DROP, "Inclusion of a forbidden file (%s).", filename); + sp_terminate(); + } + config = config->next; + } + } +} + +static void sp_execute_ex(zend_execute_data *execute_data) { + if (NULL == execute_data->func->common.function_name) { + goto execute; + } + + if (true == should_disable(execute_data)) { + return; + } + + if (execute_data->func->op_array.type == ZEND_EVAL_CODE) { + sp_log_debug("Currently in an eval\n"); + } + + if (NULL != execute_data->func->op_array.filename) { + if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) { + terminate_if_writable(ZSTR_VAL(execute_data->func->op_array.filename)); + } +} + +execute: + orig_execute_ex(execute_data); +} + +static int sp_stream_open(const char *filename, + zend_file_handle *handle) { + const zend_execute_data *data = EG(current_execute_data); + + if ((NULL != data) && (NULL != data->opline) && + (ZEND_INCLUDE_OR_EVAL == data->opline->opcode)) { + if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) { + terminate_if_writable(filename); + } + check_inclusion_regexp(filename); + } + return orig_zend_stream_open(filename, handle); +} + +int hook_execute(void) { + TSRMLS_FETCH(); + + /* zend_execute_ex is used for "classic" function calls */ + orig_execute_ex = zend_execute_ex; + zend_execute_ex = sp_execute_ex; + + /* zend_stream_open_function is used FIXME */ + orig_zend_stream_open = zend_stream_open_function; + zend_stream_open_function = sp_stream_open; + + /* zend_execute_internal is used for "indirect" functions call, + * like array_map or call_user_func. */ + + return SUCCESS; +} diff --git a/src/sp_execute.h b/src/sp_execute.h new file mode 100644 index 00000000..83457362 --- /dev/null +++ b/src/sp_execute.h @@ -0,0 +1,6 @@ +#ifndef SP_EXECUTE_H +#define SP_EXECUTE_H + +int hook_execute(void); + +#endif /* SP_EXECUTE_H */ diff --git a/src/sp_harden_rand.c b/src/sp_harden_rand.c new file mode 100644 index 00000000..e0e35fff --- /dev/null +++ b/src/sp_harden_rand.c @@ -0,0 +1,77 @@ +#include "php_snuffleupagus.h" + +extern ZEND_API zend_class_entry *zend_ce_error; + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) + +/* This function is needed because `rand` and `mt_rand` parameters + * are optional, while the ones from `random_int` aren't. */ +static void random_int_wrapper(INTERNAL_FUNCTION_PARAMETERS) { + zend_long min, max, result; + + switch (EX_NUM_ARGS()) { + case 0: + min = 0; + max = PHP_MT_RAND_MAX; + break; + case 1: + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 1, 1); + Z_PARAM_LONG(min); + ZEND_PARSE_PARAMETERS_END(); + max = PHP_MT_RAND_MAX; + break; + case 2: + default: + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 0, 2); + Z_PARAM_LONG(min); + Z_PARAM_LONG(max); + ZEND_PARSE_PARAMETERS_END(); + } + + if (min > max) { + if (php_random_int_throw(max, min, &result) == FAILURE) { + return; + } + } else { + if (php_random_int_throw(min, max, &result) == FAILURE) { + return; + } + } + + RETURN_LONG(result); +} + +PHP_FUNCTION(sp_rand) { + void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS); + + if ((orig_handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook), "rand", + strlen("rand")))) { + /* call the original `rand` function, + * since we might no be the only ones to hook it*/ + orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } + + random_int_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +PHP_FUNCTION(sp_mt_rand) { + void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS); + + if ((orig_handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook), + "mt_rand", strlen("mt_rand")))) { + /* call the original `mt_rand` function, + * since we might no be the only ones to hook it*/ + orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } + + random_int_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +int hook_rand() { + TSRMLS_FETCH(); + + HOOK_FUNCTION("rand", sp_internal_functions_hook, PHP_FN(sp_rand), false); + HOOK_FUNCTION("mt_rand", sp_internal_functions_hook, PHP_FN(sp_mt_rand), false); + + return SUCCESS; +} diff --git a/src/sp_harden_rand.h b/src/sp_harden_rand.h new file mode 100644 index 00000000..53ebdd0d --- /dev/null +++ b/src/sp_harden_rand.h @@ -0,0 +1,10 @@ +#ifndef __SP_HARDEN_RAND_H +#define __SP_HARDEN_RAND_H + +#include "ext/standard/php_rand.h" +#include "ext/standard/php_random.h" +#include "zend_exceptions.h" + +int hook_rand(); + +#endif /* __SP_HARDEN_RAND_H */ diff --git a/src/sp_list.c b/src/sp_list.c new file mode 100644 index 00000000..04154b71 --- /dev/null +++ b/src/sp_list.c @@ -0,0 +1,37 @@ +#include "sp_list.h" +#include +#include +#include "php_snuffleupagus.h" + +void sp_list_free(sp_node_t *node) { + while(node) { + sp_node_t *tmp = node->next; + pefree(node, 1); + node = tmp; + } +} + +sp_node_t *sp_new_list() { + sp_node_t *new = pecalloc(sizeof(*new), 1, 1); + new->next = new->data = new->head = NULL; + return new; +} + +void sp_list_insert(sp_node_t *list, void *data) { + if (list->head == NULL) { + list->data = data; + list->next = NULL; + list->head = list; + } else { + sp_node_t *new = pecalloc(sizeof(*new), 1, 1); + + new->data = data; + new->next = NULL; + new->head = list; + + while (list->next) { + list = list->next; + } + list->next = new; + } +} diff --git a/src/sp_list.h b/src/sp_list.h new file mode 100644 index 00000000..48b11f68 --- /dev/null +++ b/src/sp_list.h @@ -0,0 +1,15 @@ +#ifndef SP_LIST_H +#define SP_LIST_H + +typedef struct sp_node_s { + struct sp_node_s *next; + struct sp_node_s *head; + void *data; + +} sp_node_t; + +sp_node_t *sp_new_list(); +void sp_list_insert(sp_node_t *, void *); +void sp_list_free(sp_node_t *); + +#endif diff --git a/src/sp_network_utils.c b/src/sp_network_utils.c new file mode 100644 index 00000000..28bc3243 --- /dev/null +++ b/src/sp_network_utils.c @@ -0,0 +1,159 @@ +#include +#include +#include + +#include "php_snuffleupagus.h" + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) + +static inline bool cidr4_match(const struct in_addr addr, + const struct in_addr net, uint8_t bits); +static inline bool cidr6_match(const struct in6_addr address, + const struct in6_addr network, uint8_t bits); +static inline int get_ip_version(const char *ip); + +void apply_mask_on_ip(char *out, const char *const remote_addr) { + uint8_t mask4 = SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv4; + uint8_t mask6 = SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv6; + const int ip_version = get_ip_version(remote_addr); + + memset(out, 0, 128); + + if (ip_version == AF_INET) { + struct in_addr out4; + inet_pton(AF_INET, remote_addr, &out4); + const long n = out4.s_addr & htonl(0xFFFFFFFFu << (32 - mask4)); + out[0] = (n >> 24) & 0xFF; + out[1] = (n >> 16) & 0xFF; + out[2] = (n >> 8) & 0xFF; + out[3] = (n >> 0) & 0xFF; + } else if (ip_version == AF_INET6) { + inet_pton(AF_INET6, remote_addr, out); + uint32_t *p_ip = (uint32_t *)out; + while (32 < mask6) { + *p_ip = 0xFFFFFFFFu; + p_ip++; + mask6 -= 32; + } + if (0 != mask6) { + *p_ip = htonl(0xFFFFFFFFu << (32 - mask6)); + } + } else { + sp_log_err("ip_mask", "It seems that %s isn't a valid ip.", remote_addr); + } +} + +/* http://fxr.watson.org/fxr/source/include/net/xfrm.h?v=linux-2.6#L840 */ +static inline bool cidr4_match(const struct in_addr addr, + const struct in_addr net, uint8_t bits) { + if (bits == 0) { // C99 6.5.7 (3): u32 << 32 is undefined behaviour + return true; + } + return !((addr.s_addr ^ net.s_addr) & htonl(0xFFFFFFFFu << (32 - bits))); +} + +static inline bool cidr6_match(const struct in6_addr address, + const struct in6_addr network, uint8_t bits) { + //#ifdef LINUX + const uint32_t *a = address.s6_addr32; + const uint32_t *n = network.s6_addr32; + /* +#else + const uint32_t *a = address.__u6_addr.__u6_addr32; + const uint32_t *n = network.__u6_addr.__u6_addr32; +#endif +*/ + int bits_whole, bits_incomplete; + bits_whole = bits >> 5; // number of whole u32 + bits_incomplete = bits & 0x1F; // number of bits in incomplete u32 + if (bits_whole) { + if (memcmp(a, n, bits_whole << 2)) { + return false; + } + } + if (bits_incomplete) { + uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete)); + if ((a[bits_whole] ^ n[bits_whole]) & mask) { + return false; + } + } + return true; +} + +static inline int get_ip_version(const char *ip) { + struct in_addr out4; + struct in6_addr out6; + int res = inet_pton(AF_INET, ip, &out4); + if (0 == res) { + if (1 == inet_pton(AF_INET6, ip, &out6)) { + return AF_INET6; + } else { + return -1; + } + } else if (1 == res) { + return AF_INET; + } else { + return -1; + } +} + +// TODO factorise a bit this function +bool cidr_match(const char *ip, const sp_cidr *cidr) { + struct in_addr out4; + struct in6_addr out6; + + switch (get_ip_version(ip)) { + case AF_INET: + if (AF_INET != cidr->ip_version) { + return false; + } + inet_pton(AF_INET, ip, &out4); + return cidr4_match(out4, cidr->ip.ipv4, cidr->mask); + case AF_INET6: + if (AF_INET6 != cidr->ip_version) { + return false; + } + inet_pton(AF_INET6, ip, &out6); + return cidr6_match(out6, cidr->ip.ipv6, cidr->mask); + default: + sp_log_err("cidr_match", "Weird ip (%s) family", ip); + break; + } + return false; +} + +int get_ip_and_cidr(char *ip, sp_cidr *cidr) { + errno = 0; + char *mask = strchr(ip, '/'); + + if (NULL == mask) { + sp_log_err("config", + "'%s' isn't a valid network mask, it seems that you forgot a '/'.", + ip); + return -1; + } + + if (sscanf(mask + 1, "%hhu", &(cidr->mask)) != 1) { + sp_log_err("config", "'%s' isn't a valid network mask.", mask + 1); + return -1; + } + + ip[mask - ip] = '\0'; // NULL the '/' char + + cidr->ip_version = get_ip_version(ip); + + if (AF_INET == cidr->ip_version) { + if (cidr->mask > 32) { + sp_log_err("config", "'%d' isn't a valid ipv4 mask.", cidr->mask); + return -1; + } + inet_pton(AF_INET, ip, &(cidr->ip.ipv4)); + } else if (AF_INET6 == cidr->ip_version) { + inet_pton(AF_INET6, ip, &(cidr->ip.ipv6)); + } else { + return -1; + } + + ip[mask - ip] = '/'; + return 0; +} diff --git a/src/sp_network_utils.h b/src/sp_network_utils.h new file mode 100644 index 00000000..6b6ce92f --- /dev/null +++ b/src/sp_network_utils.h @@ -0,0 +1,8 @@ +#ifndef SP_NETWORK_UTILS_H +#define SP_NETWORK_UTILS_H + +int get_ip_and_cidr(char *, sp_cidr *); +bool cidr_match(const char *, const sp_cidr *); +void apply_mask_on_ip(char *, const char * const); + +#endif /*SP_NETWORK_UTILS_H*/ diff --git a/src/sp_unserialize.c b/src/sp_unserialize.c new file mode 100644 index 00000000..b5b67b42 --- /dev/null +++ b/src/sp_unserialize.c @@ -0,0 +1,111 @@ +#include "php_snuffleupagus.h" + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) + +PHP_FUNCTION(sp_serialize) { + void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS); + + /* Call the original `serialize` function. */ + if ((orig_handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook), + "serialize", 9))) { + orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } else { + sp_log_err("disabled_functions", + "Unable to find the pointer to the original function 'serialize' in " + "the hashtable.\n"); + } + + /* Compute the HMAC of the textual representation of the serialized data*/ + zval func_name; + zval hmac; + zval params[3]; + + ZVAL_STRING(&func_name, "hash_hmac"); + ZVAL_STRING(¶ms[0], "sha256"); + params[1] = *return_value; + ZVAL_STRING(¶ms[2], + SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key); + call_user_function(CG(function_table), NULL, &func_name, &hmac, 3, params); + + size_t len = Z_STRLEN_P(return_value) + Z_STRLEN(hmac); + zend_string *res = zend_string_alloc(len, 0); + + memcpy(ZSTR_VAL(res), Z_STRVAL_P(return_value), Z_STRLEN_P(return_value)); + memcpy(ZSTR_VAL(res) + Z_STRLEN_P(return_value), Z_STRVAL(hmac), + Z_STRLEN(hmac)); + ZSTR_VAL(res)[len] = '\0'; + + /* Append the computed HMAC to the serialized data. */ + return_value->value.str = res; + return; +} + +PHP_FUNCTION(sp_unserialize) { + void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS); + + char *buf = NULL; + char *serialized_str = NULL; + char *hmac = NULL; + zval expected_hmac; + size_t buf_len = 0; + zval *opts = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &buf, &buf_len, &opts) == + FAILURE) { + RETURN_FALSE; + } + + /* 64 is the length of HMAC-256 */ + if (buf_len < 64) { + sp_log_msg("unserialize", LOG_DROP, "The serialized object is too small."); + RETURN_FALSE; + } + + hmac = buf + buf_len - 64; + serialized_str = ecalloc(sizeof(*serialized_str) * (buf_len - 64 + 1), 1); + memcpy(serialized_str, buf, buf_len - 64); + + zval func_name; + ZVAL_STRING(&func_name, "hash_hmac"); + + zval params[3]; + ZVAL_STRING(¶ms[0], "sha256"); + ZVAL_STRING(¶ms[1], serialized_str); + ZVAL_STRING(¶ms[2], + SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key); + call_user_function(CG(function_table), NULL, &func_name, &expected_hmac, 3, + params); + + unsigned int status = 0; + for (uint8_t i = 0; i < 64; i++) { + status |= (hmac[i] ^ (Z_STRVAL(expected_hmac))[i]); + } + + if (0 == status) { + if ((orig_handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook), + "unserialize", 11))) { + orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } + } else { + if ( true == SNUFFLEUPAGUS_G(config).config_unserialize->simulation) { + sp_log_msg("unserialize", LOG_NOTICE, "Invalid HMAC for %s", serialized_str); + if ((orig_handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook), + "unserialize", 11))) { + orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } + } else { + sp_log_msg("unserialize", LOG_DROP, "Invalid HMAC for %s", serialized_str); + } + } + efree(serialized_str); + return; +} + +int hook_serialize(void) { + TSRMLS_FETCH(); + + HOOK_FUNCTION("serialize", sp_internal_functions_hook, PHP_FN(sp_serialize), false); + HOOK_FUNCTION("unserialize", sp_internal_functions_hook, PHP_FN(sp_unserialize), false); + + return SUCCESS; +} diff --git a/src/sp_unserialize.h b/src/sp_unserialize.h new file mode 100644 index 00000000..557c60c4 --- /dev/null +++ b/src/sp_unserialize.h @@ -0,0 +1,9 @@ +#ifndef SP_UNSERIALIZE_H +#define SP_UNSERIALIZE_H + +int hook_serialize(void); + +PHP_FUNCTION(sp_serialize); +PHP_FUNCTION(sp_unserialize); + +#endif /* SP_UNSERIALIZE_H */ diff --git a/src/sp_upload_validation.c b/src/sp_upload_validation.c new file mode 100644 index 00000000..bbd7eae9 --- /dev/null +++ b/src/sp_upload_validation.c @@ -0,0 +1,92 @@ +#include "php_snuffleupagus.h" +#include "rfc1867.h" + +ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus); + +#define EFREE_3(env) \ + for (size_t i = 0; i < 4; i++) { \ + efree(env[i]); \ + } + +void hook_upload() { + sp_rfc1867_orig_callback = php_rfc1867_callback; + php_rfc1867_callback = sp_rfc1867_callback; +} + +int sp_rfc1867_callback(unsigned int event, void *event_data, void **extra) { + int retval = SUCCESS; + + if (sp_rfc1867_orig_callback) { + retval = sp_rfc1867_orig_callback(event, event_data, extra); + } + + if (event == MULTIPART_EVENT_END) { + zend_string *file_key __attribute__((unused)) = NULL; + zval *file; + pid_t pid; + + sp_log_debug( + "Got %d files", + zend_hash_num_elements(Z_ARRVAL(PG(http_globals)[TRACK_VARS_FILES]))); + + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(PG(http_globals)[TRACK_VARS_FILES]), + file_key, file) { // for each uploaded file + + char *filename = + Z_STRVAL_P(zend_hash_str_find(Z_ARRVAL_P(file), "name", 4)); + char *tmp_name = + Z_STRVAL_P(zend_hash_str_find(Z_ARRVAL_P(file), "tmp_name", 8)); + size_t filesize = + Z_LVAL_P(zend_hash_str_find(Z_ARRVAL_P(file), "size", 4)); + char *cmd[3] = {0}; + char *env[5] = {0}; + + sp_log_debug("Filename: %s\nTmpname: %s\nSize: %d\nError: %d\nScript: %s", + filename, tmp_name, filesize, + Z_LVAL_P(zend_hash_str_find(Z_ARRVAL_P(file), "error", 5)), + SNUFFLEUPAGUS_G(config).config_upload_validation->script); + + cmd[0] = SNUFFLEUPAGUS_G(config).config_upload_validation->script; + cmd[1] = tmp_name; + cmd[2] = NULL; + + spprintf(&env[0], 0, "SP_FILENAME=%s", filename); + spprintf(&env[1], 0, "SP_REMOTE_ADDR=%s", sp_getenv("REMOTE_ADDR")); + spprintf(&env[2], 0, "SP_CURRENT_FILE=%s", + zend_get_executed_filename(TSRMLS_C)); + spprintf(&env[3], 0, "SP_FILESIZE=%zu", filesize); + env[4] = NULL; + + if ((pid = fork()) == 0) { + if (execve(SNUFFLEUPAGUS_G(config).config_upload_validation->script, + cmd, env) == -1) { + sp_log_err("upload_validation", "Could not call '%s' : %s", + SNUFFLEUPAGUS_G(config).config_upload_validation->script, + strerror(errno)); + EFREE_3(env); + exit(1); + } + } else if (pid == -1) { + sp_log_err("upload_validation", "Could not fork process : %s\n", + strerror(errno)); + EFREE_3(env); + continue; + } + + EFREE_3(env); + int waitstatus; + wait(&waitstatus); + if (WEXITSTATUS(waitstatus) != 0) { // Nope + char *uri = sp_getenv("REQUEST_URI"); + int sim = SNUFFLEUPAGUS_G(config).config_upload_validation->simulation; + sp_log_msg("upload_valiation", sim?LOG_NOTICE:LOG_DROP, + "The upload of %s on %s was rejected.", filename, uri?uri:"?"); + if (!SNUFFLEUPAGUS_G(config).config_upload_validation->simulation) { + zend_bailout(); + } + } + } + ZEND_HASH_FOREACH_END(); + } + return retval; +} diff --git a/src/sp_upload_validation.h b/src/sp_upload_validation.h new file mode 100644 index 00000000..3d595275 --- /dev/null +++ b/src/sp_upload_validation.h @@ -0,0 +1,9 @@ +#ifndef __SP_UPLOAD_VALIDATION_H__ +#define __SP_UPLOAD_VALIDATION_H__ + +void hook_upload(); + +int (*sp_rfc1867_orig_callback)(unsigned int event, void *event_data, void **extra); +int sp_rfc1867_callback(unsigned int event, void *event_data, void **extra); + +#endif diff --git a/src/sp_utils.c b/src/sp_utils.c new file mode 100644 index 00000000..087f4314 --- /dev/null +++ b/src/sp_utils.c @@ -0,0 +1,425 @@ +#include "php_snuffleupagus.h" + +#include +#include +#include +#include +#include + +static inline void _sp_log_err(const char* fmt, ...) { + char* msg; + va_list args; + + va_start(args, fmt); + vspprintf(&msg, 0, fmt, args); + va_end(args); + php_log_err(msg); +} + +void sp_log_msg(char const *feature, char const *level, const char* fmt, ...) { + char* msg; + va_list args; + + va_start(args, fmt); + vspprintf(&msg, 0, fmt, args); + va_end(args); + + char const * const client_ip = sp_getenv("REMOTE_ADDR"); + _sp_log_err("[snuffleupagus][%s][%s][%s] %s", client_ip?client_ip:"0.0.0.0", + feature, level, msg); +} + +int compute_hash(const char* const filename, char* file_hash) { + unsigned char buf[1024]; + unsigned char digest[SHA256_SIZE]; + PHP_SHA256_CTX context; + size_t n; + + php_stream* stream = + php_stream_open_wrapper(filename, "rb", REPORT_ERRORS, NULL); + if (!stream) { + sp_log_err("hash_computation", "Can not open the file %s to compute its hash.\n", filename); + return -1; + } + + PHP_SHA256Init(&context); + while ((n = php_stream_read(stream, (char*)buf, sizeof(buf))) > 0) { + PHP_SHA256Update(&context, buf, n); + } + PHP_SHA256Final(digest, &context); + php_stream_close(stream); + make_digest_ex(file_hash, digest, SHA256_SIZE); + return 0; +} + +static void construct_filename(char* filename, const char* folder) { + time_t t = time(NULL); + struct tm* tm = localtime(&t); // FIXME use `localtime_r` instead + struct timeval tval; + struct stat st = {0}; + + if (-1 == stat(folder, &st)) { + mkdir(folder, 0700); + } + + memcpy(filename, folder, strlen(folder)); + strcat(filename, "sp_dump_"); + strftime(filename + strlen(filename), 27, "%F_%T:", tm); + gettimeofday(&tval, NULL); + sprintf(filename + strlen(filename), "%04ld", tval.tv_usec); + strcat(filename, "_"); + + char* remote_addr = getenv("REMOTE_ADDR"); + if (remote_addr) { + strcat(filename, remote_addr); + } else { + strcat(filename, "0.0.0.0"); + } + strcat(filename, ".dump"); +} + +int sp_log_request(const char* folder) { + FILE* file; + const char* current_filename = zend_get_executed_filename(TSRMLS_C); + const int current_line = zend_get_executed_lineno(TSRMLS_C); + char filename[MAX_FOLDER_LEN] = {0}; + const struct { + const char* str; + const int key; + } zones[] = {{"GET", TRACK_VARS_GET}, {"POST", TRACK_VARS_POST}, + {"COOKIE", TRACK_VARS_COOKIE}, {"SERVER", TRACK_VARS_SERVER}, + {"ENV", TRACK_VARS_ENV}, {NULL, 0}}; + + construct_filename(filename, folder); + if (NULL == (file = fopen(filename, "a"))) { + sp_log_err("request_logging", "Unable to open %s", filename); + return -1; + } + + fprintf(file, "%s:%d\n", current_filename, current_line); + for (size_t i = 0; i < (sizeof(zones) / sizeof(zones[0])) - 1; i++) { + zval* variable_value; + zend_string* variable_key; + size_t params_len = strlen(zones[i].str) + 1; + char* param; + size_t size_max = 2048; + + if (Z_TYPE(PG(http_globals)[zones[i].key]) == IS_UNDEF) { + continue; + } + + const HashTable* ht = Z_ARRVAL(PG(http_globals)[zones[i].key]); + + // Compute the size of the allocation + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, variable_key, variable_value) { + params_len += snprintf(NULL, 0, "%s=%s&", ZSTR_VAL(variable_key), + Z_STRVAL_P(variable_value)); + } + ZEND_HASH_FOREACH_END(); + + params_len = params_len>size_max?size_max:params_len; + +#define NCAT_AND_DEC(a, b, c) strncat(a, b, c); c -= strlen(b); + + // Allocate and copy the data + // FIXME Why are we even allocating? + param = pecalloc(params_len, 1, 0); + NCAT_AND_DEC(param, zones[i].str, params_len); + NCAT_AND_DEC(param, ":", params_len); + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, variable_key, variable_value) { + NCAT_AND_DEC(param, ZSTR_VAL(variable_key), params_len); + NCAT_AND_DEC(param, "=", params_len); + NCAT_AND_DEC(param, Z_STRVAL_P(variable_value), params_len); + NCAT_AND_DEC(param, "&", params_len); + } + ZEND_HASH_FOREACH_END(); + + param[strlen(param) - 1] = '\0'; + + fputs(param, file); + fputs("\n", file); + } + fclose(file); + +#undef CAT_AND_DEC + return 0; +} + +static char *zv_str_to_char(zval *zv) { + zval copy; + char *ret; + + + ZVAL_ZVAL(©, zv, 1, 0); + // str = zend_string_dup(Z_STR_P(zv), 0); + for (size_t i = 0; i < Z_STRLEN(copy); i++) { + if (Z_STRVAL(copy)[i] == '\0') { + Z_STRVAL(copy)[i] = '0'; + } + } + ret = estrdup(Z_STRVAL(copy)); + // zend_string_release(str); + return ret; +} + + +char* sp_convert_to_string(zval* zv) { + switch (Z_TYPE_P(zv)) { + case IS_FALSE: + return estrdup("FALSE"); + case IS_TRUE: + return estrdup("TRUE"); + case IS_NULL: + return estrdup("NULL"); + case IS_LONG: { + char *msg; + spprintf(&msg, 0, ZEND_LONG_FMT, Z_LVAL_P(zv)); + return msg; + } + case IS_DOUBLE: { + char *msg; + spprintf(&msg, 0, "%f", Z_DVAL_P(zv)); + return msg; + } + case IS_STRING:{ + return zv_str_to_char(zv); + } + case IS_OBJECT: + return estrdup("OBJECT"); + case IS_ARRAY: + return estrdup("ARRAY"); + case IS_RESOURCE: + return estrdup("RESOURCE"); + } + return estrdup(""); +} + +bool sp_match_value(const char* value, const char* to_match, const pcre* rx) { + if (to_match) { + if (0 == strcmp(value, to_match)) { + return true; + } + } else if (rx) { + int substrvec[30]; + int ret = pcre_exec(rx, NULL, value, strlen(value), 0, 0, substrvec, 30); + + if (ret < 0) { + if (ret != PCRE_ERROR_NOMATCH) { + sp_log_err("regexp", "Something went wrong with a regexp."); + return false; + } + return false; + } + return true; + } + return false; +} + +void sp_log_disable(const char* restrict path, const char* restrict arg_name, + const char* restrict arg_value, + const sp_disabled_function* config_node) { + const char* dump = config_node->dump; + const char* alias = config_node->alias; + const int sim = config_node->simulation; + if (arg_name) { + if (alias) { + sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP, + "The call to the function '%s' in %s:%d has been disabled, " + "because its argument '%s' content (%s) matched the rule '%s'.", + path, zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C), arg_name, arg_value?arg_value:"?", + alias); + } else { + sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP, + "The call to the function '%s' in %s:%d has been disabled, " + "because its argument '%s' content (%s) matched a rule.", + path, zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C), arg_name, + arg_value?arg_value:"?"); + } + } else { + if (alias) { + sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP, + "The call to the function '%s' in %s:%d has been disabled, " + "because of the the rule '%s'.",path, + zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C), alias); + } else { + sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP, + "The call to the function '%s' in %s:%d has been disabled.", + path, zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C)); + } + } + if (dump) { + sp_log_request(config_node->dump); + } +} + +void sp_log_disable_ret(const char* restrict path, + const char* restrict ret_value, + const sp_disabled_function* config_node) { + const char* dump = config_node->dump; + const char* alias = config_node->alias; + const int sim = config_node->simulation; + if (alias) { + sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP, + "The execution has been aborted in %s:%d, " + "because the function '%s' returned '%s', which matched the rule '%s'.", + zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C), path, ret_value?ret_value:"?", alias); + } else { + sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP, + "The execution has been aborted in %s:%d, " + "because the return value (%s) of the function '%s' matched a rule.", + zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C), ret_value?ret_value:"?", path); + } + if (dump) { + sp_log_request(dump); + } +} + +int sp_match_array_key(const zval* zv, const char* to_match, const pcre* rx) { + zend_string* key; + zval* value; + char* arg_value_str; + + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zv), key, value) { + if (Z_TYPE_P(value) == IS_ARRAY) { + continue; + } + arg_value_str = sp_convert_to_string(value); + if (!sp_match_value(arg_value_str, to_match, rx)) { + efree(arg_value_str); + continue; + } else { + efree(arg_value_str); + return 1; + } + } + ZEND_HASH_FOREACH_END(); + + (void)key; // silence a compiler warning + + return 0; +} + +int sp_match_array_key_recurse(const zval* arr, sp_node_t* keys, + const char* to_match, const pcre* rx) { + zend_string* key; + zval* value; + sp_node_t* current = keys; + if (current == NULL) { + return 0; + } + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(arr), key, value) { + if (Z_TYPE_P(value) == IS_ARRAY && !strcmp(ZSTR_VAL(key), current->data)) { + return sp_match_array_key_recurse(value, current->next, to_match, rx); + } + if (!strcmp(ZSTR_VAL(key), current->data) && current->next == NULL) { + if (!to_match && !rx) { + return 1; + } + if (Z_TYPE_P(value) == IS_ARRAY) { + return sp_match_array_key(value, to_match, rx); + } else { + char *value_str = sp_convert_to_string(value); + if (sp_match_value(value_str, to_match, rx)) { + efree(value_str); + return 1; + } else { + efree (value_str); + return 0; + } + } + } + } + ZEND_HASH_FOREACH_END(); + return 0; +} + +zend_always_inline char* sp_getenv(char* var) { + if (sapi_module.getenv) { + return sapi_module.getenv(ZEND_STRL(var)); + } else { + return getenv(var); + } +} + +zend_always_inline int is_regexp_matching(const pcre* regexp, const char* str) { + int vec[30]; + int ret = pcre_exec(regexp, NULL, str, strlen(str), 0, 0, vec, sizeof(vec)); + if (ret < 0) { + if (ret != PCRE_ERROR_NOMATCH) { + sp_log_err("regexp", "Something went wrong with a regexp."); + } + return false; + } + return true; +} + +int hook_function(const char* original_name, HashTable* hook_table, + void (*new_function)(INTERNAL_FUNCTION_PARAMETERS), + bool hook_execution_table) { + zend_internal_function* func; + HashTable *ht = hook_execution_table==true?EG(function_table):CG(function_table); + + /* The `mb` module likes to hook functions, like strlen->mb_strlen, + * so we have to hook both of them. */ + if (0 == strncmp(original_name, "mb_", 3)) { + CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN; + if (zend_hash_str_find(ht, + VAR_AND_LEN(original_name + 3))) { + hook_function(original_name + 3, hook_table, new_function, hook_execution_table); + } + } else { // TODO this can be moved somewhere else to gain some marginal perfs + CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN; + char* mb_name = pecalloc(strlen(original_name) + 3 + 1, 1, 0); + memcpy(mb_name, "mb_", 3); + memcpy(mb_name + 3, VAR_AND_LEN(original_name)); + if (zend_hash_str_find(CG(function_table), VAR_AND_LEN(mb_name))) { + hook_function(mb_name, hook_table, new_function, hook_execution_table); + } + } + + if ((func = zend_hash_str_find_ptr(CG(function_table), + VAR_AND_LEN(original_name)))) { + if (func->handler == new_function) { + /* Success !*/ + } else if (zend_hash_str_add_new_ptr((hook_table), + VAR_AND_LEN(original_name), + func->handler) == NULL) { + sp_log_err("function_pointer_saving", + "Could not save function pointer for %s", original_name); + return FAILURE; + } else { + func->handler = new_function; + } + } + return SUCCESS; +} + +int hook_regexp(const pcre* regexp, HashTable* hook_table, + void (*new_function)(INTERNAL_FUNCTION_PARAMETERS), + bool hook_execution_table) { + zend_string* key; + HashTable *ht = hook_execution_table==true?EG(function_table):CG(function_table); + + ZEND_HASH_FOREACH_STR_KEY(ht, key) { + if (key) { + int vec[30]; + int ret = pcre_exec(regexp, NULL, key->val, key->len, 0, 0, vec, 30); + if (ret < 0) { /* Error or no match*/ + if (PCRE_ERROR_NOMATCH != ret) { + sp_log_err("pcre", "Runtime error with pcre, error code: %d", ret); + return FAILURE; + } + continue; + } + hook_function(key->val, hook_table, new_function, hook_execution_table); + } + } + ZEND_HASH_FOREACH_END(); + return SUCCESS; +} diff --git a/src/sp_utils.h b/src/sp_utils.h new file mode 100644 index 00000000..37dd2c0f --- /dev/null +++ b/src/sp_utils.h @@ -0,0 +1,68 @@ +#ifndef SP_UTILS_H +#define SP_UTILS_H + +#include "sp_config.h" +#include "sp_list.h" + +#if defined(__GNUC__) +# if __GNUC__ >= 3 +# define sp_pure __attribute__((pure)) +# define sp_const __attribute__((const)) +# else +# define sp_pure +# define sp_const +# endif +#endif +/* The dump filename are of the form + * `sp_dump_DATE_IPADDR.dump`, with: + * - DATE being the output of asctime, 26 chars long + * - IP_ADDR being an IP adress, with a maximum size of 15 + * + * We keep one char for the terminal \0, and one for the leading slash. + */ + +#define MAX_FOLDER_LEN \ + PATH_MAX - 1 - sizeof("sp_dump_") - 26 - sizeof("_") - 15 - \ + sizeof(".dump") - 1 + +#define VAR_AND_LEN(var) var, strlen(var) + +#define SHA256_SIZE 32 + +#define HOOK_FUNCTION(original_name, hook_table, new_function, execution) \ + hook_function(original_name, SNUFFLEUPAGUS_G(hook_table), new_function, execution) + +#define HOOK_FUNCTION_BY_REGEXP(regexp, hook_table, new_function, execution) \ + hook_regexp(regexp, SNUFFLEUPAGUS_G(hook_table), new_function, execution) + +#define LOG_NOTICE "notice" +#define LOG_DROP "drop" +#define LOG_DEBUG "debug" +#define LOG_ERROR "error" + +#define sp_log_err(feature, ...) sp_log_msg(feature, LOG_ERROR, __VA_ARGS__) +#ifdef SP_DEBUG + #define sp_log_debug(...) sp_log_msg("DEBUG", LOG_DEBUG, __VA_ARGS__) +#else + #define sp_log_debug(...) +#endif + +void sp_log_msg(char const *feature, char const *level, const char* fmt, ...); +int compute_hash(const char *const filename, char *file_hash); +char *sp_convert_to_string(zval *); +bool sp_match_value(const char *, const char *, const pcre *); +int sp_match_array_key(const zval *, const char *, const pcre *); +int sp_match_array_key_recurse(const zval *, sp_node_t *, const char *, + const pcre *); +void sp_log_disable(const char *restrict, const char *restrict, + const char *restrict, const sp_disabled_function *); +void sp_log_disable_ret(const char *restrict, const char *restrict, + const sp_disabled_function *); +char *sp_getenv(char *); +int is_regexp_matching(const pcre *, const char *); +int hook_function(const char *, HashTable *, + void (*)(INTERNAL_FUNCTION_PARAMETERS), bool); +int hook_regexp(const pcre *, HashTable *, + void (*)(INTERNAL_FUNCTION_PARAMETERS), bool); + +#endif /* SP_UTILS_H */ diff --git a/src/tests/broken_conf.phpt b/src/tests/broken_conf.phpt new file mode 100644 index 00000000..ae0ef6e2 --- /dev/null +++ b/src/tests/broken_conf.phpt @@ -0,0 +1,10 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Invalid configuration prefix for 'this is a broken line'. + diff --git a/src/tests/broken_conf2.phpt b/src/tests/broken_conf2.phpt new file mode 100644 index 00000000..88a22328 --- /dev/null +++ b/src/tests/broken_conf2.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf2.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Invalid configuration section 'sp.wrong'. diff --git a/src/tests/broken_conf_config_regexp.phpt b/src/tests/broken_conf_config_regexp.phpt new file mode 100644 index 00000000..75bc6038 --- /dev/null +++ b/src/tests/broken_conf_config_regexp.phpt @@ -0,0 +1,10 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_config_regexp.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Failed to compile '*.': nothing to repeat. +[snuffleupagus][0.0.0.0][config][error] '.filename_r()' is expecting a valid regexp, and not '"*."'. diff --git a/src/tests/broken_conf_enable_disable.phpt b/src/tests/broken_conf_enable_disable.phpt new file mode 100644 index 00000000..2f3fe195 --- /dev/null +++ b/src/tests/broken_conf_enable_disable.phpt @@ -0,0 +1,9 @@ +--TEST-- +Global strict mode +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/borken_conf_enable_disable.ini +--FILE-- +--EXPECTF-- +[snuffleupagus][0.0.0.0][config][error] A rule can't be enabled and disabled. diff --git a/src/tests/broken_conf_expecting_bool.phpt b/src/tests/broken_conf_expecting_bool.phpt new file mode 100644 index 00000000..80e1b61d --- /dev/null +++ b/src/tests/broken_conf_expecting_bool.phpt @@ -0,0 +1,9 @@ +--TEST-- +Bad boolean value in configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_expecting_bool.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Trailing chars '337);' at the end of '.enable(1337);'. diff --git a/src/tests/broken_conf_expecting_int.phpt b/src/tests/broken_conf_expecting_int.phpt new file mode 100644 index 00000000..e8063370 --- /dev/null +++ b/src/tests/broken_conf_expecting_int.phpt @@ -0,0 +1,9 @@ +--TEST-- +Bad integer value in configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_expecting_int.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][error][error] .mask_ipv4() is expecting a valid integer. diff --git a/src/tests/broken_conf_invalid_cidr.phpt b/src/tests/broken_conf_invalid_cidr.phpt new file mode 100644 index 00000000..515091b0 --- /dev/null +++ b/src/tests/broken_conf_invalid_cidr.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_invalid_cidr.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] '42' isn't a valid ipv4 mask. diff --git a/src/tests/broken_conf_invalid_cidr6.phpt b/src/tests/broken_conf_invalid_cidr6.phpt new file mode 100644 index 00000000..d20cfcd0 --- /dev/null +++ b/src/tests/broken_conf_invalid_cidr6.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_invalid_cidr6.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] 'ZZZ' isn't a valid network mask. diff --git a/src/tests/broken_conf_invalid_cidr6_no_slash.phpt b/src/tests/broken_conf_invalid_cidr6_no_slash.phpt new file mode 100644 index 00000000..de70a05b --- /dev/null +++ b/src/tests/broken_conf_invalid_cidr6_no_slash.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration, invalid cidr for ipv6 because there is no `/` in it +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_invalid_cidr6_no_slash.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] '2001:0db8:0000:0000:0000:ff00:0042:8329' isn't a valid network mask, it seems that you forgot a '/'. diff --git a/src/tests/broken_conf_invalid_cidr6_too_big.phpt b/src/tests/broken_conf_invalid_cidr6_too_big.phpt new file mode 100644 index 00000000..47d4a5de --- /dev/null +++ b/src/tests/broken_conf_invalid_cidr6_too_big.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration, cidr for ipv6 is too big, that will `mod` to 25. +(13337%128 = 25) +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_invalid_cidr6_too_big.ini +--FILE-- +--EXPECT-- diff --git a/src/tests/broken_conf_invalid_cidr_value.phpt b/src/tests/broken_conf_invalid_cidr_value.phpt new file mode 100644 index 00000000..712f123a --- /dev/null +++ b/src/tests/broken_conf_invalid_cidr_value.phpt @@ -0,0 +1,11 @@ +--TEST-- +Broken configuration, invalid cidr value +(13337%128 = 25) +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_invalid_cidr_value.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][error][error] There is an issue with the parsing of '"': it doesn't look like a valid string. +[snuffleupagus][0.0.0.0][config][error] " doesn't contain a valid cidr. diff --git a/src/tests/broken_conf_invalid_type.phpt b/src/tests/broken_conf_invalid_type.phpt new file mode 100644 index 00000000..29d2ff57 --- /dev/null +++ b/src/tests/broken_conf_invalid_type.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken conf with wrong type +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_invalid_type.ini +--FILE-- +--EXPECTF-- +[snuffleupagus][0.0.0.0][error][error] There is an issue with the parsing of '"totally_wrong"_type")': it doesn't look like a valid string. diff --git a/src/tests/broken_conf_line_empty_string.phpt b/src/tests/broken_conf_line_empty_string.phpt new file mode 100644 index 00000000..c4334b97 --- /dev/null +++ b/src/tests/broken_conf_line_empty_string.phpt @@ -0,0 +1,9 @@ +--TEST-- +Configuration line with an empty string +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_line_empty_string.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][error][error] There is an issue with the parsing of '': it doesn't look like a valid string. diff --git a/src/tests/broken_conf_line_no_closing.phpt b/src/tests/broken_conf_line_no_closing.phpt new file mode 100644 index 00000000..07c94e41 --- /dev/null +++ b/src/tests/broken_conf_line_no_closing.phpt @@ -0,0 +1,9 @@ +--TEST-- +Configuration line without closing parenthese +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_line_no_closing.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][error][error] There is an issue with the parsing of '"123"': it doesn't look like a valid string. diff --git a/src/tests/broken_conf_line_too_long.phpt b/src/tests/broken_conf_line_too_long.phpt new file mode 100644 index 00000000..8e82708c --- /dev/null +++ b/src/tests/broken_conf_line_too_long.phpt @@ -0,0 +1,10 @@ +--TEST-- +Line too long in configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_line_too_long.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] The following line is too long: 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111);. +[snuffleupagus][0.0.0.0][error][error] .mask_ipv4() is expecting a valid integer. diff --git a/src/tests/broken_conf_lots_of_quotes.phpt b/src/tests/broken_conf_lots_of_quotes.phpt new file mode 100644 index 00000000..e877cfa4 --- /dev/null +++ b/src/tests/broken_conf_lots_of_quotes.phpt @@ -0,0 +1,9 @@ +--TEST-- +Configuration line with too many quotes +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_lots_of_quotes.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][error][error] There is an issue with the parsing of '"this\"is a weird\"\"\"cookie\"name"");': it doesn't look like a valid string. diff --git a/src/tests/broken_conf_mutually_exclusive.phpt b/src/tests/broken_conf_mutually_exclusive.phpt new file mode 100644 index 00000000..9de7e5a5 --- /dev/null +++ b/src/tests/broken_conf_mutually_exclusive.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Invalid configuration line: 'sp.disabled_functions.function("system").param("id").value("42").value_r("^id$").drop();':'.value' and '.regexp' are mutually exclusives. \ No newline at end of file diff --git a/src/tests/broken_conf_mutually_exclusive2.phpt b/src/tests/broken_conf_mutually_exclusive2.phpt new file mode 100644 index 00000000..9d3ea361 --- /dev/null +++ b/src/tests/broken_conf_mutually_exclusive2.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive2.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Invalid configuration line: 'sp.disabled_functions.function("system").function_r("system").param("id").value("42").drop();': '.r_function' and '.function' are mutually exclusive. \ No newline at end of file diff --git a/src/tests/broken_conf_mutually_exclusive3.phpt b/src/tests/broken_conf_mutually_exclusive3.phpt new file mode 100644 index 00000000..58686a31 --- /dev/null +++ b/src/tests/broken_conf_mutually_exclusive3.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive3.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Invalid configuration line: 'sp.disabled_functions.function("system").param("id").value("42").filename_r("^id$").filename("pouet.txt").drop();':'.r_filename' and '.filename' are mutually exclusive. \ No newline at end of file diff --git a/src/tests/broken_conf_mutually_exclusive4.phpt b/src/tests/broken_conf_mutually_exclusive4.phpt new file mode 100644 index 00000000..d854380d --- /dev/null +++ b/src/tests/broken_conf_mutually_exclusive4.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive4.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Invalid configuration line: 'sp.disabled_functions.function("system").param("id").value("42").param_r("^id$").drop();':'.r_param' and '.param' are mutually exclusive. \ No newline at end of file diff --git a/src/tests/broken_conf_mutually_exclusive5.phpt b/src/tests/broken_conf_mutually_exclusive5.phpt new file mode 100644 index 00000000..a265c30e --- /dev/null +++ b/src/tests/broken_conf_mutually_exclusive5.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive5.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Invalid configuration line: 'sp.disabled_functions.function("system").ret("0").drop().ret_r("^0$");':'.r_ret' and '.ret' are mutually exclusive. \ No newline at end of file diff --git a/src/tests/broken_conf_mutually_exclusive6.phpt b/src/tests/broken_conf_mutually_exclusive6.phpt new file mode 100644 index 00000000..d0cdb855 --- /dev/null +++ b/src/tests/broken_conf_mutually_exclusive6.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive6.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Invalid configuration line: 'sp.disabled_functions.function("system").param("id").value("42").ret_r("^0$").drop();':`ret` and `param` are mutually exclusives. \ No newline at end of file diff --git a/src/tests/broken_conf_mutually_exclusive7.phpt b/src/tests/broken_conf_mutually_exclusive7.phpt new file mode 100644 index 00000000..c9a35134 --- /dev/null +++ b/src/tests/broken_conf_mutually_exclusive7.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive7.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Invalid configuration line: 'sp.disabled_functions.function("system").ret("0").drop().allow();': The rule must either be a `drop` or and `allow` one. \ No newline at end of file diff --git a/src/tests/broken_conf_mutually_exclusive8.phpt b/src/tests/broken_conf_mutually_exclusive8.phpt new file mode 100644 index 00000000..7c5baeeb --- /dev/null +++ b/src/tests/broken_conf_mutually_exclusive8.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive8.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Invalid configuration line: 'sp.disabled_functions.ret("0").drop();': must take a function name. \ No newline at end of file diff --git a/src/tests/broken_conf_no_closing_misc.phpt b/src/tests/broken_conf_no_closing_misc.phpt new file mode 100644 index 00000000..1d1e112b --- /dev/null +++ b/src/tests/broken_conf_no_closing_misc.phpt @@ -0,0 +1,10 @@ +--TEST-- +Configuration line without closing parenthese, misc +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_no_closing_misc.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Missing closing ) in line 123. +[snuffleupagus][0.0.0.0][error][error] .mask_ipv4() is expecting a valid integer. diff --git a/src/tests/broken_conf_weird_keyword.phpt b/src/tests/broken_conf_weird_keyword.phpt new file mode 100644 index 00000000..52937913 --- /dev/null +++ b/src/tests/broken_conf_weird_keyword.phpt @@ -0,0 +1,9 @@ +--TEST-- +Bad config, unknown keyword +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_weird_keyword.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][config][error] Trailing chars '.not_a_valid_keyword("test");' at the end of '.enable().not_a_valid_keyword("test");'. \ No newline at end of file diff --git a/src/tests/broken_conf_wrong_quotes.phpt b/src/tests/broken_conf_wrong_quotes.phpt new file mode 100644 index 00000000..b6324fea --- /dev/null +++ b/src/tests/broken_conf_wrong_quotes.phpt @@ -0,0 +1,9 @@ +--TEST-- +Configuration line with too many quotes +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_wrong_quotes.ini +--FILE-- +--EXPECT-- +[snuffleupagus][0.0.0.0][error][error] There is an issue with the parsing of '"\)': it doesn't look like a valid string. diff --git a/src/tests/broken_conf_wrong_type.phpt b/src/tests/broken_conf_wrong_type.phpt new file mode 100644 index 00000000..338ca3a7 --- /dev/null +++ b/src/tests/broken_conf_wrong_type.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken conf with wrong type +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_conf_wrong_type.ini +--FILE-- +--EXPECTF-- +[snuffleupagus][0.0.0.0][error][error] .ret_type() is expecting a valid php type ('false', 'true', 'array'. 'object', 'long', 'double', 'null', 'resource', 'reference', 'undef'). diff --git a/src/tests/broken_regexp.phpt b/src/tests/broken_regexp.phpt new file mode 100644 index 00000000..cbfef7df --- /dev/null +++ b/src/tests/broken_regexp.phpt @@ -0,0 +1,9 @@ +--TEST-- +Broken regexp +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/broken_regexp.ini +--FILE-- +--EXPECTF-- +[snuffleupagus][0.0.0.0][config][error] '.value_r()' is expecting a valid regexp, and not '"^$["'. diff --git a/src/tests/config/borken_conf_enable_disable.ini b/src/tests/config/borken_conf_enable_disable.ini new file mode 100644 index 00000000..4e952940 --- /dev/null +++ b/src/tests/config/borken_conf_enable_disable.ini @@ -0,0 +1 @@ +sp.global_strict.disable().enable(); diff --git a/src/tests/config/broken_conf.ini b/src/tests/config/broken_conf.ini new file mode 100644 index 00000000..0595320e --- /dev/null +++ b/src/tests/config/broken_conf.ini @@ -0,0 +1 @@ +this is a broken line diff --git a/src/tests/config/broken_conf2.ini b/src/tests/config/broken_conf2.ini new file mode 100644 index 00000000..fdb6b8f0 --- /dev/null +++ b/src/tests/config/broken_conf2.ini @@ -0,0 +1 @@ +sp.wrong diff --git a/src/tests/config/broken_conf_expecting_bool.ini b/src/tests/config/broken_conf_expecting_bool.ini new file mode 100644 index 00000000..51c28b2f --- /dev/null +++ b/src/tests/config/broken_conf_expecting_bool.ini @@ -0,0 +1,5 @@ + # this is an example of broken conf + + + ; this is another comment +sp.harden_random.enable(1337); diff --git a/src/tests/config/broken_conf_expecting_int.ini b/src/tests/config/broken_conf_expecting_int.ini new file mode 100644 index 00000000..8e2efea7 --- /dev/null +++ b/src/tests/config/broken_conf_expecting_int.ini @@ -0,0 +1,2 @@ +sp.global.secret_key("abcdef"); +sp.cookie_encryption.cookie("super_cookie").mask_ipv4(abc); diff --git a/src/tests/config/broken_conf_invalid_cidr.ini b/src/tests/config/broken_conf_invalid_cidr.ini new file mode 100644 index 00000000..0cdc695a --- /dev/null +++ b/src/tests/config/broken_conf_invalid_cidr.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").drop().cidr("127.0.0.1/42"); diff --git a/src/tests/config/broken_conf_invalid_cidr6.ini b/src/tests/config/broken_conf_invalid_cidr6.ini new file mode 100644 index 00000000..e5a120c7 --- /dev/null +++ b/src/tests/config/broken_conf_invalid_cidr6.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").drop().cidr("2001:0db8:0000:0000:0000:ff00:0042:8329/ZZZ"); diff --git a/src/tests/config/broken_conf_invalid_cidr6_no_slash.ini b/src/tests/config/broken_conf_invalid_cidr6_no_slash.ini new file mode 100644 index 00000000..e4cf8355 --- /dev/null +++ b/src/tests/config/broken_conf_invalid_cidr6_no_slash.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").drop().cidr("2001:0db8:0000:0000:0000:ff00:0042:8329"); diff --git a/src/tests/config/broken_conf_invalid_cidr6_too_big.ini b/src/tests/config/broken_conf_invalid_cidr6_too_big.ini new file mode 100644 index 00000000..417dee71 --- /dev/null +++ b/src/tests/config/broken_conf_invalid_cidr6_too_big.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").drop().cidr("2001:0db8:0000:0000:0000:ff00:0042:8329/13337"); diff --git a/src/tests/config/broken_conf_invalid_cidr_value.ini b/src/tests/config/broken_conf_invalid_cidr_value.ini new file mode 100644 index 00000000..733e8891 --- /dev/null +++ b/src/tests/config/broken_conf_invalid_cidr_value.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").drop().cidr(" diff --git a/src/tests/config/broken_conf_invalid_type.ini b/src/tests/config/broken_conf_invalid_type.ini new file mode 100644 index 00000000..b2cd8cd3 --- /dev/null +++ b/src/tests/config/broken_conf_invalid_type.ini @@ -0,0 +1 @@ +sp.disable_functions.function("strpos").ret_type("totally_wrong"_type") diff --git a/src/tests/config/broken_conf_line_empty_string.ini b/src/tests/config/broken_conf_line_empty_string.ini new file mode 100644 index 00000000..74d0e5a2 --- /dev/null +++ b/src/tests/config/broken_conf_line_empty_string.ini @@ -0,0 +1 @@ +sp.cookie_encryption.mask_ipv4(123).cookie( diff --git a/src/tests/config/broken_conf_line_no_closing.ini b/src/tests/config/broken_conf_line_no_closing.ini new file mode 100644 index 00000000..bcac291c --- /dev/null +++ b/src/tests/config/broken_conf_line_no_closing.ini @@ -0,0 +1 @@ +sp.cookie_encryption.mask_ipv4(123).cookie("123" diff --git a/src/tests/config/broken_conf_line_too_long.ini b/src/tests/config/broken_conf_line_too_long.ini new file mode 100644 index 00000000..ed057a50 --- /dev/null +++ b/src/tests/config/broken_conf_line_too_long.ini @@ -0,0 +1 @@ +sp.cookie_encryption.cookie("super_cookie").mask_ipv4(1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111); diff --git a/src/tests/config/broken_conf_lots_of_quotes.ini b/src/tests/config/broken_conf_lots_of_quotes.ini new file mode 100644 index 00000000..dfd48e74 --- /dev/null +++ b/src/tests/config/broken_conf_lots_of_quotes.ini @@ -0,0 +1 @@ +sp.cookie_encryption.mask_ipv4(123).cookie("this\"is a weird\"\"\"cookie\"name""); diff --git a/src/tests/config/broken_conf_mutually_exclusive.ini b/src/tests/config/broken_conf_mutually_exclusive.ini new file mode 100644 index 00000000..af1d5058 --- /dev/null +++ b/src/tests/config/broken_conf_mutually_exclusive.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").param("id").value("42").value_r("^id$").drop(); diff --git a/src/tests/config/broken_conf_mutually_exclusive2.ini b/src/tests/config/broken_conf_mutually_exclusive2.ini new file mode 100644 index 00000000..29b21d47 --- /dev/null +++ b/src/tests/config/broken_conf_mutually_exclusive2.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").function_r("system").param("id").value("42").drop(); diff --git a/src/tests/config/broken_conf_mutually_exclusive3.ini b/src/tests/config/broken_conf_mutually_exclusive3.ini new file mode 100644 index 00000000..556de08e --- /dev/null +++ b/src/tests/config/broken_conf_mutually_exclusive3.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").param("id").value("42").filename_r("^id$").filename("pouet.txt").drop(); diff --git a/src/tests/config/broken_conf_mutually_exclusive4.ini b/src/tests/config/broken_conf_mutually_exclusive4.ini new file mode 100644 index 00000000..d212ad43 --- /dev/null +++ b/src/tests/config/broken_conf_mutually_exclusive4.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").param("id").value("42").param_r("^id$").drop(); diff --git a/src/tests/config/broken_conf_mutually_exclusive5.ini b/src/tests/config/broken_conf_mutually_exclusive5.ini new file mode 100644 index 00000000..5b64079b --- /dev/null +++ b/src/tests/config/broken_conf_mutually_exclusive5.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").ret("0").drop().ret_r("^0$"); diff --git a/src/tests/config/broken_conf_mutually_exclusive6.ini b/src/tests/config/broken_conf_mutually_exclusive6.ini new file mode 100644 index 00000000..d08ee58b --- /dev/null +++ b/src/tests/config/broken_conf_mutually_exclusive6.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").param("id").value("42").ret_r("^0$").drop(); diff --git a/src/tests/config/broken_conf_mutually_exclusive7.ini b/src/tests/config/broken_conf_mutually_exclusive7.ini new file mode 100644 index 00000000..645c26ce --- /dev/null +++ b/src/tests/config/broken_conf_mutually_exclusive7.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").ret("0").drop().allow(); diff --git a/src/tests/config/broken_conf_mutually_exclusive8.ini b/src/tests/config/broken_conf_mutually_exclusive8.ini new file mode 100644 index 00000000..b08ef578 --- /dev/null +++ b/src/tests/config/broken_conf_mutually_exclusive8.ini @@ -0,0 +1 @@ +sp.disable_functions.ret("0").drop(); diff --git a/src/tests/config/broken_conf_no_closing_misc.ini b/src/tests/config/broken_conf_no_closing_misc.ini new file mode 100644 index 00000000..2cb79a82 --- /dev/null +++ b/src/tests/config/broken_conf_no_closing_misc.ini @@ -0,0 +1 @@ +sp.cookie_encryption.cookie("123").mask_ipv4(123 diff --git a/src/tests/config/broken_conf_to_few_args.ini b/src/tests/config/broken_conf_to_few_args.ini new file mode 100644 index 00000000..89e19be4 --- /dev/null +++ b/src/tests/config/broken_conf_to_few_args.ini @@ -0,0 +1 @@ +sp.harden_random.enable(); diff --git a/src/tests/config/broken_conf_weird_keyword.ini b/src/tests/config/broken_conf_weird_keyword.ini new file mode 100644 index 00000000..bf5e7f5c --- /dev/null +++ b/src/tests/config/broken_conf_weird_keyword.ini @@ -0,0 +1 @@ +sp.harden_random.enable().not_a_valid_keyword("test"); diff --git a/src/tests/config/broken_conf_wrong_quotes.ini b/src/tests/config/broken_conf_wrong_quotes.ini new file mode 100644 index 00000000..c8cc9493 --- /dev/null +++ b/src/tests/config/broken_conf_wrong_quotes.ini @@ -0,0 +1 @@ +sp.cookie_encryption.mask_ipv4(123).cookie("\) diff --git a/src/tests/config/broken_conf_wrong_type.ini b/src/tests/config/broken_conf_wrong_type.ini new file mode 100644 index 00000000..6ecca6a6 --- /dev/null +++ b/src/tests/config/broken_conf_wrong_type.ini @@ -0,0 +1,5 @@ +sp.disable_functions.function("strpos").ret_type("undef").drop().alias("Return value is undef"); +sp.disable_functions.function("strpos").ret_type("null").drop().alias("Return value is null"); +sp.disable_functions.function("strpos").ret_type("object").drop().alias("Return value is object"); +sp.disable_functions.function("strpos").ret_type("reference").drop().alias("Return value is reference"); +sp.disable_functions.function("strpos").ret_type("totally_wrong_type").drop().alias("Return value is FALSE"); diff --git a/src/tests/config/broken_config_regexp.ini b/src/tests/config/broken_config_regexp.ini new file mode 100644 index 00000000..efad83ef --- /dev/null +++ b/src/tests/config/broken_config_regexp.ini @@ -0,0 +1 @@ +sp.disable_functions.function_r("^system$").filename_r("*.").drop(); diff --git a/src/tests/config/broken_regexp.ini b/src/tests/config/broken_regexp.ini new file mode 100644 index 00000000..8e1f69a9 --- /dev/null +++ b/src/tests/config/broken_regexp.ini @@ -0,0 +1 @@ +sp.disable_functions.function("AwesomeClass::method3").param("a").drop().value_r("^$["); diff --git a/src/tests/config/config_disable_writable.ini b/src/tests/config/config_disable_writable.ini new file mode 100644 index 00000000..9f906019 --- /dev/null +++ b/src/tests/config/config_disable_writable.ini @@ -0,0 +1 @@ + sp.readonly_exec.enable(); diff --git a/src/tests/config/config_disable_writable_disabled.ini b/src/tests/config/config_disable_writable_disabled.ini new file mode 100644 index 00000000..6a33437b --- /dev/null +++ b/src/tests/config/config_disable_writable_disabled.ini @@ -0,0 +1 @@ + sp.readonly_exec.disable(); diff --git a/src/tests/config/config_disable_writable_simulation.ini b/src/tests/config/config_disable_writable_simulation.ini new file mode 100644 index 00000000..52a43ba4 --- /dev/null +++ b/src/tests/config/config_disable_writable_simulation.ini @@ -0,0 +1 @@ + sp.readonly_exec.enable().simulation(); diff --git a/src/tests/config/config_disabled_functions_filename_r.ini b/src/tests/config/config_disabled_functions_filename_r.ini new file mode 100644 index 00000000..b92f1360 --- /dev/null +++ b/src/tests/config/config_disabled_functions_filename_r.ini @@ -0,0 +1,2 @@ +sp.disable_functions.function_r("^system$").filename_r("\\.txt$").drop(); +sp.disable_functions.function_r("^shell_exec$").filename_r("\\.php$").drop(); diff --git a/src/tests/config/config_disabled_functions_method.ini b/src/tests/config/config_disabled_functions_method.ini new file mode 100644 index 00000000..4d088d20 --- /dev/null +++ b/src/tests/config/config_disabled_functions_method.ini @@ -0,0 +1,3 @@ +sp.disable_functions.function("AwesomeClass::method1").drop(); +sp.disable_functions.function("method2").drop(); +sp.disable_functions.function("AwesomeClass::method3").param("a").value("pouet").drop(); diff --git a/src/tests/config/config_disabled_functions_name_r.ini b/src/tests/config/config_disabled_functions_name_r.ini new file mode 100644 index 00000000..3f7178e0 --- /dev/null +++ b/src/tests/config/config_disabled_functions_name_r.ini @@ -0,0 +1,2 @@ +sp.disable_functions.function_r("^not_system$").ret("42").drop(); +sp.disable_functions.function_r("^system$").ret("1337").drop(); diff --git a/src/tests/config/config_disabled_functions_name_type.ini b/src/tests/config/config_disabled_functions_name_type.ini new file mode 100644 index 00000000..2b433dfe --- /dev/null +++ b/src/tests/config/config_disabled_functions_name_type.ini @@ -0,0 +1 @@ +sp.disable_functions.function_r("^strcmp$").param("str1").param_type("array").drop(); diff --git a/src/tests/config/config_disabled_functions_namespace.ini b/src/tests/config/config_disabled_functions_namespace.ini new file mode 100644 index 00000000..d09b81bb --- /dev/null +++ b/src/tests/config/config_disabled_functions_namespace.ini @@ -0,0 +1,2 @@ +sp.disable_functions.function("strcmp").drop(); +sp.disable_functions.function("my_super_namespace::my_function").drop(); diff --git a/src/tests/config/config_disabled_functions_nul_byte.ini b/src/tests/config/config_disabled_functions_nul_byte.ini new file mode 100644 index 00000000..79945835 --- /dev/null +++ b/src/tests/config/config_disabled_functions_nul_byte.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").param("command").value_r("id").drop(); \ No newline at end of file diff --git a/src/tests/config/config_disabled_functions_param.ini b/src/tests/config/config_disabled_functions_param.ini new file mode 100644 index 00000000..7363781e --- /dev/null +++ b/src/tests/config/config_disabled_functions_param.ini @@ -0,0 +1,6 @@ +sp.disable_functions.function("system").param("command").value_r("^id$").alias("1").drop(); +sp.disable_functions.function("array_sum").param("array").value_r("^8$").alias("2").drop(); +sp.disable_functions.function("shell_exec").param("cmd").value("id").alias("3").drop(); +sp.disable_functions.function("shell_exec").param("cmd").value("bla").alias("4").drop(); +sp.disable_functions.function("strcmp").param("str1").value("bla").alias("5").drop().simulation(); +sp.disable_functions.function("strncmp").param("str1").value("bla").drop().simulation(); diff --git a/src/tests/config/config_disabled_functions_param_alias.ini b/src/tests/config/config_disabled_functions_param_alias.ini new file mode 100644 index 00000000..f8d9f439 --- /dev/null +++ b/src/tests/config/config_disabled_functions_param_alias.ini @@ -0,0 +1,2 @@ +sp.disable_functions.function("system").alias("1").drop(); +sp.disable_functions.function("shell_exec").alias("2").drop().simulation(); diff --git a/src/tests/config/config_disabled_functions_param_allow.ini b/src/tests/config/config_disabled_functions_param_allow.ini new file mode 100644 index 00000000..e349b386 --- /dev/null +++ b/src/tests/config/config_disabled_functions_param_allow.ini @@ -0,0 +1,3 @@ +sp.disable_functions.function("system").param("command").value("echo win").filename("test.php").drop(); +sp.disable_functions.function("system").param("command").value("echo win").allow(); +sp.disable_functions.function("system").drop(); diff --git a/src/tests/config/config_disabled_functions_param_array.ini b/src/tests/config/config_disabled_functions_param_array.ini new file mode 100644 index 00000000..7b716925 --- /dev/null +++ b/src/tests/config/config_disabled_functions_param_array.ini @@ -0,0 +1,4 @@ +sp.disable_functions.function("foo").param("arr").value("abcd").alias("1").drop(); +sp.disable_functions.function("foo").param("arr[bla]").value("abcdef").alias("2").drop(); +sp.disable_functions.function("foo").param("arr[test]").alias("3").drop(); +sp.disable_functions.function("foo").param("arr[test2][foo][lol]").value("aaa").alias("4").drop(); diff --git a/src/tests/config/config_disabled_functions_param_int.ini b/src/tests/config/config_disabled_functions_param_int.ini new file mode 100644 index 00000000..2552f0a2 --- /dev/null +++ b/src/tests/config/config_disabled_functions_param_int.ini @@ -0,0 +1,2 @@ +sp.disable_functions.function("foobar").param("id").value("42").drop(); +sp.disable_functions.function("foobar").param("id").value_r("^1337").drop(); diff --git a/src/tests/config/config_disabled_functions_param_r.ini b/src/tests/config/config_disabled_functions_param_r.ini new file mode 100644 index 00000000..d9f6692e --- /dev/null +++ b/src/tests/config/config_disabled_functions_param_r.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").param_r("^command$").value("id").drop(); diff --git a/src/tests/config/config_disabled_functions_param_runtime.ini b/src/tests/config/config_disabled_functions_param_runtime.ini new file mode 100644 index 00000000..641bd0ad --- /dev/null +++ b/src/tests/config/config_disabled_functions_param_runtime.ini @@ -0,0 +1 @@ +sp.disable_functions.function("test").param("param").value_r("1337").drop(); diff --git a/src/tests/config/config_disabled_functions_param_str_representation.ini b/src/tests/config/config_disabled_functions_param_str_representation.ini new file mode 100644 index 00000000..7171a301 --- /dev/null +++ b/src/tests/config/config_disabled_functions_param_str_representation.ini @@ -0,0 +1 @@ +sp.disable_functions.function("var_export").param("var").value("bla").drop(); diff --git a/src/tests/config/config_disabled_functions_require.ini b/src/tests/config/config_disabled_functions_require.ini new file mode 100644 index 00000000..474fadac --- /dev/null +++ b/src/tests/config/config_disabled_functions_require.ini @@ -0,0 +1 @@ +sp.disable_functions.function("require").param("").value_r("meh$").drop(); diff --git a/src/tests/config/config_disabled_functions_ret_allow.ini b/src/tests/config/config_disabled_functions_ret_allow.ini new file mode 100644 index 00000000..18842271 --- /dev/null +++ b/src/tests/config/config_disabled_functions_ret_allow.ini @@ -0,0 +1,2 @@ +sp.disable_functions.function("strpos").hash("70b33f3eaf585b245640bb2c92445d0040b2bcb31395aa25dede9f2df4dbcbe8").allow(); +sp.disable_functions.function("strpos").drop(); diff --git a/src/tests/config/config_disabled_functions_ret_allow_value.ini b/src/tests/config/config_disabled_functions_ret_allow_value.ini new file mode 100644 index 00000000..e179819b --- /dev/null +++ b/src/tests/config/config_disabled_functions_ret_allow_value.ini @@ -0,0 +1 @@ +sp.disable_functions.function("strpos").ret("0").allow(); diff --git a/src/tests/config/config_disabled_functions_ret_right_hash.ini b/src/tests/config/config_disabled_functions_ret_right_hash.ini new file mode 100644 index 00000000..6f49177b --- /dev/null +++ b/src/tests/config/config_disabled_functions_ret_right_hash.ini @@ -0,0 +1,4 @@ +sp.disable_functions.function("system").ret("1").drop(); +sp.disable_functions.function("system").ret("1337").hash("123456789597a81a2b862cdb49920e2cba2e5979a3fc374c58c803e8f5c99a10").drop(); +sp.disable_functions.function("system").ret("1338").hash("522a976fa597a81a2b862cdb49920e2cba2e5979a3fc374c58c803e8f5c99a10").drop(); +sp.disable_functions.function("system").ret("1337").hash("522a976fa597a81a2b862cdb49920e2cba2e5979a3fc374c58c803e8f5c99a10").drop(); diff --git a/src/tests/config/config_disabled_functions_ret_simulation.ini b/src/tests/config/config_disabled_functions_ret_simulation.ini new file mode 100644 index 00000000..ee46c4be --- /dev/null +++ b/src/tests/config/config_disabled_functions_ret_simulation.ini @@ -0,0 +1,3 @@ +sp.disable_functions.function("strpos").ret("0").simulation().drop(); +sp.disable_functions.function("stripos").ret("0").simulation().drop().alias("1"); +sp.disable_functions.function("strcmp").ret("0").drop(); diff --git a/src/tests/config/config_disabled_functions_right_hash.ini b/src/tests/config/config_disabled_functions_right_hash.ini new file mode 100644 index 00000000..fab68fa6 --- /dev/null +++ b/src/tests/config/config_disabled_functions_right_hash.ini @@ -0,0 +1,3 @@ +sp.disable_functions.function("system").hash("1337c3ad8cf096272cd0e78768af3b11325f498de5c2c36f40adc43643af378a").allow(); +sp.disable_functions.function("system").hash("d259c3ad8cf096272cd0e78768af3b11325f498de5c2c36f40adc43643af378a").allow(); +sp.disable_functions.function("system").drop(); \ No newline at end of file diff --git a/src/tests/config/config_disabled_user_functions.ini b/src/tests/config/config_disabled_user_functions.ini new file mode 100644 index 00000000..15cbccc8 --- /dev/null +++ b/src/tests/config/config_disabled_user_functions.ini @@ -0,0 +1 @@ +sp.disable_functions.function("my_super_function").drop(); diff --git a/src/tests/config/config_encrypted_cookies.ini b/src/tests/config/config_encrypted_cookies.ini new file mode 100644 index 00000000..710e8632 --- /dev/null +++ b/src/tests/config/config_encrypted_cookies.ini @@ -0,0 +1,3 @@ +sp.global.secret_key("abcdef"); +sp.cookie_encryption.cookie("super_cookie").mask_ipv4(8).mask_ipv6(2); +sp.auto_cookie_secure.enable(); diff --git a/src/tests/config/config_noncore_function_hooking.ini b/src/tests/config/config_noncore_function_hooking.ini new file mode 100644 index 00000000..88f2acf9 --- /dev/null +++ b/src/tests/config/config_noncore_function_hooking.ini @@ -0,0 +1 @@ +sp.disable_functions.function("custom_fun").drop(); diff --git a/src/tests/config/config_rand_harden_disabled.ini b/src/tests/config/config_rand_harden_disabled.ini new file mode 100644 index 00000000..b9cd2277 --- /dev/null +++ b/src/tests/config/config_rand_harden_disabled.ini @@ -0,0 +1 @@ +sp.harden_random.disable(); diff --git a/src/tests/config/config_serialize.ini b/src/tests/config/config_serialize.ini new file mode 100644 index 00000000..f2c1699f --- /dev/null +++ b/src/tests/config/config_serialize.ini @@ -0,0 +1,2 @@ +sp.global.secret_key("abcdef"); +sp.unserialize_hmac.enable(); \ No newline at end of file diff --git a/src/tests/config/config_serialize_sim.ini b/src/tests/config/config_serialize_sim.ini new file mode 100644 index 00000000..7f015e07 --- /dev/null +++ b/src/tests/config/config_serialize_sim.ini @@ -0,0 +1,2 @@ +sp.global.secret_key("abcdef"); +sp.unserialize_hmac.enable().simulation(); diff --git a/src/tests/config/disable_xxe.ini b/src/tests/config/disable_xxe.ini new file mode 100644 index 00000000..bc9d1f2e --- /dev/null +++ b/src/tests/config/disable_xxe.ini @@ -0,0 +1 @@ +sp.disable_xxe.enable(); diff --git a/src/tests/config/disable_xxe_disable.ini b/src/tests/config/disable_xxe_disable.ini new file mode 100644 index 00000000..bb1e432e --- /dev/null +++ b/src/tests/config/disable_xxe_disable.ini @@ -0,0 +1 @@ +sp.disable_xxe.disable(); diff --git a/src/tests/config/disabled_function_local_var.ini b/src/tests/config/disabled_function_local_var.ini new file mode 100644 index 00000000..64d98dcb --- /dev/null +++ b/src/tests/config/disabled_function_local_var.ini @@ -0,0 +1,2 @@ +sp.disable_functions.function("phpinfo").var("b").value("1337").drop(); +sp.disable_functions.function("strlen").var("a").value("1337").drop(); diff --git a/src/tests/config/disabled_function_super_global_var.ini b/src/tests/config/disabled_function_super_global_var.ini new file mode 100644 index 00000000..e0c87e14 --- /dev/null +++ b/src/tests/config/disabled_function_super_global_var.ini @@ -0,0 +1 @@ +sp.disable_functions.function("strlen").var("_GET[bla]").value("test2").drop(); diff --git a/src/tests/config/disabled_functions.ini b/src/tests/config/disabled_functions.ini new file mode 100644 index 00000000..cf54164d --- /dev/null +++ b/src/tests/config/disabled_functions.ini @@ -0,0 +1,7 @@ +sp.disable_functions.function("system").drop(); +sp.disable_functions.function("vprintf").hash("123456789").drop(); +sp.disable_functions.function("printf").disable().drop(); +sp.disable_functions.function("printf").simulation().drop(); +sp.disable_functions.function("print").disable().drop(); # this is a comment +sp.disable_functions.function_r("^var_dump$").drop(); +sp.disable_functions.function("sprintf").filename("wrong file name").drop(); diff --git a/src/tests/config/disabled_functions_cidr.ini b/src/tests/config/disabled_functions_cidr.ini new file mode 100644 index 00000000..9e527bae --- /dev/null +++ b/src/tests/config/disabled_functions_cidr.ini @@ -0,0 +1,4 @@ +sp.disable_functions.function("system").drop().cidr("127.0.0.1/8"); +sp.disable_functions.function("printf").drop().cidr("10.0.0.1/8"); +sp.disable_functions.function("strpos").drop().cidr("2001:0db8:0000:0000:0000:ff00:0042:8329/24"); +sp.disable_functions.function("printf").drop().cidr("2002:0db8:0000:0000:0000:ff00:0042:8329/24"); diff --git a/src/tests/config/disabled_functions_mb.ini b/src/tests/config/disabled_functions_mb.ini new file mode 100644 index 00000000..b6afd97d --- /dev/null +++ b/src/tests/config/disabled_functions_mb.ini @@ -0,0 +1,2 @@ +sp.disable_functions.function("strlen").drop(); +sp.disable_functions.function("mb_strlen").drop(); diff --git a/src/tests/config/disabled_functions_ret.ini b/src/tests/config/disabled_functions_ret.ini new file mode 100644 index 00000000..2b769a9e --- /dev/null +++ b/src/tests/config/disabled_functions_ret.ini @@ -0,0 +1,5 @@ +sp.disable_functions.function("testFunction").ret("0").drop().disable(); +sp.disable_functions.function("strpos").ret("0").drop().filename_r(".*\\.php"); +sp.disable_functions.function_r("str[ia]pos").ret_r("^[^a-z]+$").drop(); +sp.disable_functions.function_r("stripos").ret_r("^[^a-z]+").drop(); +sp.disable_functions.function("Bob::a").ret("0").drop(); diff --git a/src/tests/config/disabled_functions_ret_type.ini b/src/tests/config/disabled_functions_ret_type.ini new file mode 100644 index 00000000..56c8e575 --- /dev/null +++ b/src/tests/config/disabled_functions_ret_type.ini @@ -0,0 +1 @@ +sp.disable_functions.function("strpos").ret_type("false").drop().alias("Return value is FALSE"); diff --git a/src/tests/config/disabled_functions_ret_type_double.ini b/src/tests/config/disabled_functions_ret_type_double.ini new file mode 100644 index 00000000..a1239d8f --- /dev/null +++ b/src/tests/config/disabled_functions_ret_type_double.ini @@ -0,0 +1 @@ +sp.disable_functions.function("cos").ret_type("double").drop().alias("Return value is a double"); diff --git a/src/tests/config/disabled_functions_ret_type_long.ini b/src/tests/config/disabled_functions_ret_type_long.ini new file mode 100644 index 00000000..6cccd4d9 --- /dev/null +++ b/src/tests/config/disabled_functions_ret_type_long.ini @@ -0,0 +1 @@ +sp.disable_functions.function("strlen").ret_type("long").drop().alias("Return value is a long"); diff --git a/src/tests/config/disabled_functions_ret_type_resource.ini b/src/tests/config/disabled_functions_ret_type_resource.ini new file mode 100644 index 00000000..e81cf2ca --- /dev/null +++ b/src/tests/config/disabled_functions_ret_type_resource.ini @@ -0,0 +1 @@ +sp.disable_functions.function("fopen").ret_type("resource").drop().alias("Return value is a resource"); diff --git a/src/tests/config/disabled_functions_ret_type_str.ini b/src/tests/config/disabled_functions_ret_type_str.ini new file mode 100644 index 00000000..b3ff0501 --- /dev/null +++ b/src/tests/config/disabled_functions_ret_type_str.ini @@ -0,0 +1 @@ +sp.disable_functions.function("substr").ret_type("string").drop().alias("Return value is a string"); diff --git a/src/tests/config/disabled_functions_ret_type_true.ini b/src/tests/config/disabled_functions_ret_type_true.ini new file mode 100644 index 00000000..02a37dd4 --- /dev/null +++ b/src/tests/config/disabled_functions_ret_type_true.ini @@ -0,0 +1 @@ +sp.disable_functions.function("is_numeric").ret_type("true").drop().alias("Return value is a true"); diff --git a/src/tests/config/disabled_functions_retval.ini b/src/tests/config/disabled_functions_retval.ini new file mode 100644 index 00000000..20422e4f --- /dev/null +++ b/src/tests/config/disabled_functions_retval.ini @@ -0,0 +1 @@ +sp.disable_functions.function("str_repeat").ret("fufufu").drop(); diff --git a/src/tests/config/disabled_functions_retval_rx.ini b/src/tests/config/disabled_functions_retval_rx.ini new file mode 100644 index 00000000..ca2bce32 --- /dev/null +++ b/src/tests/config/disabled_functions_retval_rx.ini @@ -0,0 +1 @@ +sp.disable_functions.function("str_repeat").ret_r("(fu){3}").drop(); diff --git a/src/tests/config/disabled_functions_zero_cidr.ini b/src/tests/config/disabled_functions_zero_cidr.ini new file mode 100644 index 00000000..bba1af96 --- /dev/null +++ b/src/tests/config/disabled_functions_zero_cidr.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").drop().cidr("0.0.0.0/0"); diff --git a/src/tests/config/dump_request.ini b/src/tests/config/dump_request.ini new file mode 100644 index 00000000..8c595f97 --- /dev/null +++ b/src/tests/config/dump_request.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").drop().dump("./dump_results/"); diff --git a/src/tests/config/dump_request_invalid_folder.ini b/src/tests/config/dump_request_invalid_folder.ini new file mode 100644 index 00000000..b5ae1545 --- /dev/null +++ b/src/tests/config/dump_request_invalid_folder.ini @@ -0,0 +1 @@ +sp.disable_functions.function("system").drop().dump("/root/NON_EXISTENT/FOLDER/PLEASE/"); diff --git a/src/tests/config/empty.ini b/src/tests/config/empty.ini new file mode 100644 index 00000000..e69de29b diff --git a/src/tests/config/empty_conf.ini b/src/tests/config/empty_conf.ini new file mode 100644 index 00000000..e69de29b diff --git a/src/tests/config/encryption_key_only.ini b/src/tests/config/encryption_key_only.ini new file mode 100644 index 00000000..7de44384 --- /dev/null +++ b/src/tests/config/encryption_key_only.ini @@ -0,0 +1 @@ +sp.global.secret_key("abcdef"); diff --git a/src/tests/config/global_strict.ini b/src/tests/config/global_strict.ini new file mode 100644 index 00000000..2bc2bdc1 --- /dev/null +++ b/src/tests/config/global_strict.ini @@ -0,0 +1 @@ + sp.global_strict.enable(); diff --git a/src/tests/config/global_strict_disabled.ini b/src/tests/config/global_strict_disabled.ini new file mode 100644 index 00000000..2e68471f --- /dev/null +++ b/src/tests/config/global_strict_disabled.ini @@ -0,0 +1 @@ +sp.global_strict.disable(); diff --git a/src/tests/config/harden_rand.ini b/src/tests/config/harden_rand.ini new file mode 100644 index 00000000..89e19be4 --- /dev/null +++ b/src/tests/config/harden_rand.ini @@ -0,0 +1 @@ +sp.harden_random.enable(); diff --git a/src/tests/config/upload_validation.ini b/src/tests/config/upload_validation.ini new file mode 100644 index 00000000..06461344 --- /dev/null +++ b/src/tests/config/upload_validation.ini @@ -0,0 +1,2 @@ +sp.upload_validation.script("tests/upload_ko.sh"); +sp.upload_validation.enable(); diff --git a/src/tests/config/upload_validation_invalid.ini b/src/tests/config/upload_validation_invalid.ini new file mode 100644 index 00000000..7a638a15 --- /dev/null +++ b/src/tests/config/upload_validation_invalid.ini @@ -0,0 +1 @@ +sp.upload_validation.script("./tests/data/upload_invalid.sh").enable(); diff --git a/src/tests/config/upload_validation_ko.ini b/src/tests/config/upload_validation_ko.ini new file mode 100644 index 00000000..b15977f7 --- /dev/null +++ b/src/tests/config/upload_validation_ko.ini @@ -0,0 +1 @@ +sp.upload_validation.script("./tests/data/upload_ko.sh").enable(); diff --git a/src/tests/config/upload_validation_ko_simulation.ini b/src/tests/config/upload_validation_ko_simulation.ini new file mode 100644 index 00000000..da56439f --- /dev/null +++ b/src/tests/config/upload_validation_ko_simulation.ini @@ -0,0 +1 @@ +sp.upload_validation.script("./tests/data/upload_ko.sh").enable().simulation(); diff --git a/src/tests/config/upload_validation_no_exist.ini b/src/tests/config/upload_validation_no_exist.ini new file mode 100644 index 00000000..24f81a50 --- /dev/null +++ b/src/tests/config/upload_validation_no_exist.ini @@ -0,0 +1 @@ +sp.upload_validation.script("fufufufufu").enable(); diff --git a/src/tests/config/upload_validation_non_exec.ini b/src/tests/config/upload_validation_non_exec.ini new file mode 100644 index 00000000..bdf0a57d --- /dev/null +++ b/src/tests/config/upload_validation_non_exec.ini @@ -0,0 +1 @@ +sp.upload_validation.script("tests/data/upload_no_exec.sh").enable(); diff --git a/src/tests/config/upload_validation_ok.ini b/src/tests/config/upload_validation_ok.ini new file mode 100644 index 00000000..5df8db8f --- /dev/null +++ b/src/tests/config/upload_validation_ok.ini @@ -0,0 +1 @@ +sp.upload_validation.script("./tests/data/upload_ok.sh").enable(); diff --git a/src/tests/data/upload_invalid.sh b/src/tests/data/upload_invalid.sh new file mode 100755 index 00000000..e5eb0c63 --- /dev/null +++ b/src/tests/data/upload_invalid.sh @@ -0,0 +1 @@ +lulz diff --git a/src/tests/data/upload_ko.sh b/src/tests/data/upload_ko.sh new file mode 100755 index 00000000..c4cacdc6 --- /dev/null +++ b/src/tests/data/upload_ko.sh @@ -0,0 +1,2 @@ +#!/bin/sh +exit 1; diff --git a/src/tests/data/upload_no_exec.sh b/src/tests/data/upload_no_exec.sh new file mode 100644 index 00000000..6b9cafae --- /dev/null +++ b/src/tests/data/upload_no_exec.sh @@ -0,0 +1,2 @@ +#!/bin/sh +exit 0; diff --git a/src/tests/data/upload_ok.sh b/src/tests/data/upload_ok.sh new file mode 100755 index 00000000..6b9cafae --- /dev/null +++ b/src/tests/data/upload_ok.sh @@ -0,0 +1,2 @@ +#!/bin/sh +exit 0; diff --git a/src/tests/deny_writable_execution.phpt b/src/tests/deny_writable_execution.phpt new file mode 100644 index 00000000..2870561a --- /dev/null +++ b/src/tests/deny_writable_execution.phpt @@ -0,0 +1,44 @@ +--TEST-- +Readonly execution attempt +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disable_writable.ini +--FILE-- + +--EXPECTF-- +Code execution within a non-writable file. +[snuffleupagus][0.0.0.0][readonly_exec][drop] Attempted execution of a writable file (%a/writable_file.txt). +--CLEAN-- + \ No newline at end of file diff --git a/src/tests/deny_writable_execution_disabled.phpt b/src/tests/deny_writable_execution_disabled.phpt new file mode 100644 index 00000000..6d1233bf --- /dev/null +++ b/src/tests/deny_writable_execution_disabled.phpt @@ -0,0 +1,32 @@ +--TEST-- +Readonly execution attempt +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disable_writable_disabled.ini +--FILE-- + +--EXPECT-- +Code execution within a writable file. +Code execution within a non-writable file. +--CLEAN-- + \ No newline at end of file diff --git a/src/tests/deny_writable_execution_simulation.phpt b/src/tests/deny_writable_execution_simulation.phpt new file mode 100644 index 00000000..3278be89 --- /dev/null +++ b/src/tests/deny_writable_execution_simulation.phpt @@ -0,0 +1,45 @@ +--TEST-- +Readonly execution attempt (simulation mode) +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disable_writable_simulation.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][readonly_exec][notice] Attempted execution of a writable file (%a/writable_file.txt). +Code execution within a writable file. +Code execution within a non-writable file. +--CLEAN-- + \ No newline at end of file diff --git a/src/tests/disable_xxe_dom.phpt b/src/tests/disable_xxe_dom.phpt new file mode 100644 index 00000000..47f3db3b --- /dev/null +++ b/src/tests/disable_xxe_dom.phpt @@ -0,0 +1,71 @@ +--TEST-- +Disable XXE +--SKIPIF-- + +--INI-- +extension=`php-config --extension-dir`/dom.so +sp.configuration_file={PWD}/config/disable_xxe.ini +--FILE-- + + +]> +&foo; +EOD; + +file_put_contents('content.xml', $xml); + +libxml_disable_entity_loader(true); +$dom = new DOMDocument('1.0'); +$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT); +printf("libxml_disable_entity to true: %s\n", $dom->getElementsByTagName('testing')->item(0)->nodeValue); + +libxml_disable_entity_loader(false); +$dom = new DOMDocument('1.0'); +$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT); +printf("libxml_disable_entity to false: %s\n", $dom->getElementsByTagName('testing')->item(0)->nodeValue); + +$xml = "foo"; +file_put_contents('content.xml', $xml); + +libxml_disable_entity_loader(false); +$dom = new DOMDocument('1.0'); +$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT); +printf("without xxe: %s", $dom->getElementsByTagName('testing')->item(0)->nodeValue); + +?> +--EXPECTF-- +Warning: DOMDocument::loadXML(): I/O warning : failed to load external entity "file://%a/content.txt" in %a/disable_xxe_dom.php on line %d + +Warning: DOMDocument::loadXML(): Failure to process entity foo in Entity, line: %d in %a/disable_xxe_dom.php on line %d + +Warning: DOMDocument::loadXML(): Entity 'foo' not defined in Entity, line: %d in %a/disable_xxe_dom.php on line %d + +Notice: Trying to get property of non-object in %a/disable_xxe_dom.php on line %d +libxml_disable_entity to true: + +Warning: DOMDocument::loadXML(): I/O warning : failed to load external entity "file://%a/content.txt" in %a/disable_xxe_dom.php on line %d + +Warning: DOMDocument::loadXML(): Failure to process entity foo in Entity, line: %d in %a/disable_xxe_dom.php on line %d + +Warning: DOMDocument::loadXML(): Entity 'foo' not defined in Entity, line: %d in %a/disable_xxe_dom.php on line %d + +Notice: Trying to get property of non-object in %a/disable_xxe_dom.php on line %d +libxml_disable_entity to false: +without xxe: foo +--CLEAN-- + diff --git a/src/tests/disable_xxe_dom_disabled.phpt b/src/tests/disable_xxe_dom_disabled.phpt new file mode 100644 index 00000000..b89b595f --- /dev/null +++ b/src/tests/disable_xxe_dom_disabled.phpt @@ -0,0 +1,56 @@ +--TEST-- +Disable XXE +--SKIPIF-- + +--INI-- +extension=`php-config --extension-dir`/dom.so +sp.configuration_file={PWD}/config/disable_xxe_disable.ini +--FILE-- +WARNING, external entity loaded!'; +file_put_contents($dir . '/content.txt', $content); + +$xml = << + +]> +&foo; +EOD; + +file_put_contents($dir . '/content.xml', $xml); + +libxml_disable_entity_loader(true); +$dom = new DOMDocument('1.0'); +$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT); +printf("libxml_disable_entity to true: %s\n", $dom->getElementsByTagName('testing')->item(0)->nodeValue); + +libxml_disable_entity_loader(false); +$dom = new DOMDocument('1.0'); +$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT); +printf("libxml_disable_entity to false: %s\n", $dom->getElementsByTagName('testing')->item(0)->nodeValue); + +$xml = "foo"; +file_put_contents('content.xml', $xml); + +libxml_disable_entity_loader(false); +$dom = new DOMDocument('1.0'); +$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT); +printf("without xxe: %s", $dom->getElementsByTagName('testing')->item(0)->nodeValue); + +?> +--EXPECTF-- +libxml_disable_entity to true: WARNING, external entity loaded! +libxml_disable_entity to false: WARNING, external entity loaded! +without xxe: foo +--CLEAN-- + diff --git a/src/tests/disable_xxe_simplexml.phpt b/src/tests/disable_xxe_simplexml.phpt new file mode 100644 index 00000000..54404a34 --- /dev/null +++ b/src/tests/disable_xxe_simplexml.phpt @@ -0,0 +1,52 @@ +--TEST-- +Disable XXE +--SKIPIF-- + +--INI-- +extension=`php-config --extension-dir`/simplexml.so +sp.configuration_file={PWD}/config/disable_xxe.ini +--FILE-- + + +]> +&foo; +EOD; + +file_put_contents('content.xml', $xml); + +libxml_disable_entity_loader(true); +$doc = new SimpleXMLElement($xml); +printf("libxml_disable_entity to true: %s\n", $doc->testing); + +libxml_disable_entity_loader(false); +$doc = new SimpleXMLElement($xml); +printf("libxml_disable_entity to false: %s\n", $doc->testing); + +$xml = "foo"; +file_put_contents('content.xml', $xml); + +$doc = new SimpleXMLElement($xml); +printf("without xxe: %s", $doc->testing); + +?> +--EXPECT-- +libxml_disable_entity to true: +libxml_disable_entity to false: +without xxe: foo +--CLEAN-- + diff --git a/src/tests/disable_xxe_simplexml_oop.phpt b/src/tests/disable_xxe_simplexml_oop.phpt new file mode 100644 index 00000000..62762eb1 --- /dev/null +++ b/src/tests/disable_xxe_simplexml_oop.phpt @@ -0,0 +1,52 @@ +--TEST-- +Disable XXE +--SKIPIF-- + +--INI-- +extension=`php-config --extension-dir`/simplexml.so +sp.configuration_file={PWD}/config/disable_xxe.ini +--FILE-- + + +]> +&foo; +EOD; + +file_put_contents('content.xml', $xml); + +libxml_disable_entity_loader(true); +$doc = simplexml_load_string($xml); +printf("libxml_disable_entity to true: %s\n", $doc->testing); + +libxml_disable_entity_loader(false); +$doc = simplexml_load_string($xml); +printf("libxml_disable_entity to false: %s\n", $doc->testing); + +$xml = "foo"; +file_put_contents('content.xml', $xml); + +$doc = simplexml_load_string($xml); +printf("without xxe: %s", $doc->testing); + +?> +--EXPECT-- +libxml_disable_entity to true: +libxml_disable_entity to false: +without xxe: foo +--CLEAN-- + diff --git a/src/tests/disable_xxe_xml_parse.phpt b/src/tests/disable_xxe_xml_parse.phpt new file mode 100644 index 00000000..944bc383 --- /dev/null +++ b/src/tests/disable_xxe_xml_parse.phpt @@ -0,0 +1,104 @@ +--TEST-- +Disable XXE +--SKIPIF-- + +--INI-- +extension=`php-config --extension-dir`/xml.so +sp.configuration_file={PWD}/config/disable_xxe.ini +--FILE-- + + +]> +&foo; +EOD; + +file_put_contents('content.xml', $xml); + +function create_parser() { + $parser = xml_parser_create(); + xml_set_element_handler( + $parser, + function($parser, $name, array $attributes) { + var_dump($name); + echo "\n"; + var_dump($attributes); + }, + function($parser, $name) { + var_dump($name); + } + ); + + xml_set_character_data_handler( + $parser, + function ($parser, $text){ + echo 'text' . $text; + } + ); + + return $parser; +} + +libxml_disable_entity_loader(true); +$parser = create_parser(); +$doc = xml_parse($parser, $xml, true); +xml_parser_free($parser); + +libxml_disable_entity_loader(false); +$parser = create_parser(); +$doc = xml_parse($parser, $xml, true); +xml_parser_free($parser); + +$xml = "foo"; +file_put_contents('content.xml', $xml); +$parser = create_parser(); +$doc = xml_parse($parser, $xml, true); +xml_parser_free($parser); + +--EXPECT-- +string(4) "TEST" + +array(0) { +} +string(7) "TESTING" + +array(0) { +} +string(7) "TESTING" +string(4) "TEST" +string(4) "TEST" + +array(0) { +} +string(7) "TESTING" + +array(0) { +} +string(7) "TESTING" +string(4) "TEST" +string(4) "TEST" + +array(0) { +} +string(7) "TESTING" + +array(0) { +} +textfoostring(7) "TESTING" +string(4) "TEST" +--CLEAN-- + diff --git a/src/tests/disabled_function_local_var.phpt b/src/tests/disabled_function_local_var.phpt new file mode 100644 index 00000000..3142039a --- /dev/null +++ b/src/tests/disabled_function_local_var.phpt @@ -0,0 +1,24 @@ +--TEST-- +Disable functions - match on a local variable +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_function_local_var.ini +--FILE-- + +--EXPECTF-- +Value of a: 1338 +2 +Value of a: 1337 +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'strlen' in %a/tests/disabled_function_local_var.php:%d has been disabled. \ No newline at end of file diff --git a/src/tests/disabled_function_super_global_var.phpt b/src/tests/disabled_function_super_global_var.phpt new file mode 100644 index 00000000..d41897a3 --- /dev/null +++ b/src/tests/disabled_function_super_global_var.phpt @@ -0,0 +1,20 @@ +--TEST-- +Disable functions - match on a super global +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_function_super_global_var.ini +--GET-- +bla=test +--FILE-- + +--EXPECTF-- +4 +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'strlen' in %s/tests/disabled_function_super_global_var.php:%d has been disabled. diff --git a/src/tests/disabled_functions.phpt b/src/tests/disabled_functions.phpt new file mode 100644 index 00000000..37da911c --- /dev/null +++ b/src/tests/disabled_functions.phpt @@ -0,0 +1,21 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'system' in %a/tests/disabled_functions.php:%d has been disabled. +[snuffleupagus][0.0.0.0][disabled_function][notice] The call to the function 'printf' in %a/tests/disabled_functions.php:%d has been disabled. +printf in simulation mode +print in disabled mode +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'var_dump' in %a/tests/disabled_functions.php:%d has been disabled. +1 diff --git a/src/tests/disabled_functions_cidr.phpt b/src/tests/disabled_functions_cidr.phpt new file mode 100644 index 00000000..5b131072 --- /dev/null +++ b/src/tests/disabled_functions_cidr.phpt @@ -0,0 +1,18 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--ENV-- +return << +--EXPECTF-- +[snuffleupagus][127.0.0.1][disabled_function][drop] The call to the function 'system' in %a/tests/disabled_functions_cidr.php:2 has been disabled. +1337 diff --git a/src/tests/disabled_functions_cidr_6.phpt b/src/tests/disabled_functions_cidr_6.phpt new file mode 100644 index 00000000..f2c5f5a8 --- /dev/null +++ b/src/tests/disabled_functions_cidr_6.phpt @@ -0,0 +1,18 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--ENV-- +return << +--EXPECTF-- +[snuffleupagus][2001:0db8:0000:0000:0000:ff00:0042:8328][disabled_function][drop] The call to the function 'strpos' in %a/tests/disabled_functions_cidr_6.php:2 has been disabled. +1337 diff --git a/src/tests/disabled_functions_filename_r.phpt b/src/tests/disabled_functions_filename_r.phpt new file mode 100644 index 00000000..ed468029 --- /dev/null +++ b/src/tests/disabled_functions_filename_r.phpt @@ -0,0 +1,14 @@ +--TEST-- +Disable functions - filename regexp +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_filename_r.ini +--FILE-- + +--EXPECTF-- +42 +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'shell_exec' in %a/tests/disabled_functions_filename_r.php:%d has been disabled. \ No newline at end of file diff --git a/src/tests/disabled_functions_mb.phpt b/src/tests/disabled_functions_mb.phpt new file mode 100644 index 00000000..70890639 --- /dev/null +++ b/src/tests/disabled_functions_mb.phpt @@ -0,0 +1,12 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_mb.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'strlen' in %a/tests/disabled_functions_mb.php:2 has been disabled. diff --git a/src/tests/disabled_functions_method.phpt b/src/tests/disabled_functions_method.phpt new file mode 100644 index 00000000..33651b7a --- /dev/null +++ b/src/tests/disabled_functions_method.phpt @@ -0,0 +1,29 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_method.ini +--FILE-- +method1("pif"); +$c->method2("paf"); +$c->method3("pouet"); +?> +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'AwesomeClass::method1' in %a/tests/disabled_functions_method.php:4 has been disabled. +method2:paf +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'AwesomeClass::method3' in %a/tests/disabled_functions_method.php:10 has been disabled, because its argument 'a' content (pouet) matched a rule. diff --git a/src/tests/disabled_functions_name_r.phpt b/src/tests/disabled_functions_name_r.phpt new file mode 100644 index 00000000..0e29abbb --- /dev/null +++ b/src/tests/disabled_functions_name_r.phpt @@ -0,0 +1,15 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_name_r.ini +--FILE-- + +--EXPECTF-- +42 +1337 +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/disabled_functions_name_r.php:3, because the return value (1337) of the function 'system' matched a rule. diff --git a/src/tests/disabled_functions_name_type.phpt b/src/tests/disabled_functions_name_type.phpt new file mode 100644 index 00000000..c5b24d62 --- /dev/null +++ b/src/tests/disabled_functions_name_type.phpt @@ -0,0 +1,14 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_name_type.ini +--FILE-- + +--EXPECTF-- +0 +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'strcmp' in %a/disabled_functions_name_type.php:%d has been disabled, because its argument 'str1' content (?) matched a rule. diff --git a/src/tests/disabled_functions_namespace.phpt b/src/tests/disabled_functions_namespace.phpt new file mode 100644 index 00000000..72c7d0be --- /dev/null +++ b/src/tests/disabled_functions_namespace.phpt @@ -0,0 +1,31 @@ +--TEST-- +Disable functions: namespaces support isn't implemented now +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_namespace.ini +--FILE-- + +--XFAIL-- +--EXPECTF-- +[snuffleupagus] The call to the function 'strcmp' in %a/tests/disabled_functions_namespace.php:%d has been disabled. diff --git a/src/tests/disabled_functions_noconf.phpt b/src/tests/disabled_functions_noconf.phpt new file mode 100644 index 00000000..cb134137 --- /dev/null +++ b/src/tests/disabled_functions_noconf.phpt @@ -0,0 +1,12 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/empty.ini +--FILE-- + +--EXPECT-- +1 diff --git a/src/tests/disabled_functions_nul_byte.phpt b/src/tests/disabled_functions_nul_byte.phpt new file mode 100644 index 00000000..95e87de5 --- /dev/null +++ b/src/tests/disabled_functions_nul_byte.phpt @@ -0,0 +1,15 @@ +--TEST-- +Disable functions with nul byte +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_nul_byte.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'system' in %a/tests/disabled_functions_nul_byte.php:2 has been disabled, because its argument 'command' content (0id) matched a rule. +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'system' in %a/tests/disabled_functions_nul_byte.php:3 has been disabled, because its argument 'command' content (id) matched a rule. \ No newline at end of file diff --git a/src/tests/disabled_functions_param.phpt b/src/tests/disabled_functions_param.phpt new file mode 100644 index 00000000..23092172 --- /dev/null +++ b/src/tests/disabled_functions_param.phpt @@ -0,0 +1,24 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_param.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'system' in %a/disabled_functions_param.php:2 has been disabled, because its argument 'command' content (id) matched the rule '1'. +win +int(15) +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'shell_exec' in %a/disabled_functions_param.php:5 has been disabled, because its argument 'cmd' content (id) matched the rule '3'. +42 +[snuffleupagus][0.0.0.0][disabled_function][notice] The call to the function 'strcmp' in %a/tests/disabled_functions_param.php:7 has been disabled, because its argument 'str1' content (bla) matched the rule '5'. +[snuffleupagus][0.0.0.0][disabled_function][notice] The call to the function 'strncmp' in %a/tests/disabled_functions_param.php:8 has been disabled, because its argument 'str1' content (bla) matched a rule. diff --git a/src/tests/disabled_functions_param_alias.phpt b/src/tests/disabled_functions_param_alias.phpt new file mode 100644 index 00000000..fe3d1c1a --- /dev/null +++ b/src/tests/disabled_functions_param_alias.phpt @@ -0,0 +1,14 @@ +--TEST-- +Disable functions - alias +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_param_alias.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'system' in %a/tests/disabled_functions_param_alias.php:2 has been disabled, because of the the rule '1'. +[snuffleupagus][0.0.0.0][disabled_function][notice] The call to the function 'shell_exec' in %a/tests/disabled_functions_param_alias.php:3 has been disabled, because of the the rule '2'. diff --git a/src/tests/disabled_functions_param_allow.phpt b/src/tests/disabled_functions_param_allow.phpt new file mode 100644 index 00000000..b6ff01a5 --- /dev/null +++ b/src/tests/disabled_functions_param_allow.phpt @@ -0,0 +1,14 @@ +--TEST-- +Disable functions - allow +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_param_allow.ini +--FILE-- + +--EXPECTF-- +win +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'system' in %a/tests/disabled_functions_param_allow.php:3 has been disabled. \ No newline at end of file diff --git a/src/tests/disabled_functions_param_array.phpt b/src/tests/disabled_functions_param_array.phpt new file mode 100644 index 00000000..6596d1ac --- /dev/null +++ b/src/tests/disabled_functions_param_array.phpt @@ -0,0 +1,37 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_param_array.ini +--FILE-- +"test1"); +foo($a); +$a=Array("a"=>"abcd"); +foo($a); +$a=Array("a"=>"abcde"); +foo($a); +$a=Array("bla"=>"abcdef"); +foo($a); +$a=Array("bla"=>"aaa", "a"=>"eee" ); +foo($a); +$a=Array("test"=>"aaa", "a"=>"fff" ); +foo($a); +$a=Array("test2"=>Array("foo"=>Array("lol"=>"bbb")), "a"=>"cccc"); +foo($a); +$a=Array("test2"=>Array("foo"=>Array("lol"=>"aaa")), "a"=>"dddd"); +foo($a); +?> +--EXPECTF-- +test1 +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'foo' in %a/disabled_functions_param_array.php:3 has been disabled, because its argument 'arr' content (Array) matched the rule '1'. +abcde +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'foo' in %a/disabled_functions_param_array.php:3 has been disabled, because its argument 'arr' content (Array) matched the rule '2'. +eee +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'foo' in %a/disabled_functions_param_array.php:3 has been disabled, because its argument 'arr' content (Array) matched the rule '3'. +cccc +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'foo' in %a/disabled_functions_param_array.php:3 has been disabled, because its argument 'arr' content (Array) matched the rule '4'. diff --git a/src/tests/disabled_functions_param_int.phpt b/src/tests/disabled_functions_param_int.phpt new file mode 100644 index 00000000..3b2cc08c --- /dev/null +++ b/src/tests/disabled_functions_param_int.phpt @@ -0,0 +1,25 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_param_int.ini +--FILE-- + +--EXPECTF-- +1 +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'foobar' in %a/tests/disabled_functions_param_int.php:3 has been disabled, because its argument 'id' content (42) matched a rule. +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'foobar' in %a/tests/disabled_functions_param_int.php:3 has been disabled, because its argument 'id' content (1337) matched a rule. +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'foobar' in %a/tests/disabled_functions_param_int.php:3 has been disabled, because its argument 'id' content (13374242) matched a rule. +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'foobar' in %a/tests/disabled_functions_param_int.php:3 has been disabled, because its argument 'id' content (42) matched a rule. +10 diff --git a/src/tests/disabled_functions_param_r.phpt b/src/tests/disabled_functions_param_r.phpt new file mode 100644 index 00000000..3708881e --- /dev/null +++ b/src/tests/disabled_functions_param_r.phpt @@ -0,0 +1,14 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_param_r.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'system' in %a/tests/disabled_functions_param_r.php:2 has been disabled, because its argument 'command' content (id) matched a rule. +win diff --git a/src/tests/disabled_functions_param_str_representation.phpt b/src/tests/disabled_functions_param_str_representation.phpt new file mode 100644 index 00000000..7cbdc0f8 --- /dev/null +++ b/src/tests/disabled_functions_param_str_representation.phpt @@ -0,0 +1,25 @@ +--TEST-- +Disable functions - casting various types to string internally +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_param_str_representation.ini +--FILE-- + +--EXPECTF-- +true +false +NULL +1 +1.0 +123 diff --git a/src/tests/disabled_functions_parse_class.phpt b/src/tests/disabled_functions_parse_class.phpt new file mode 100644 index 00000000..af9ed888 --- /dev/null +++ b/src/tests/disabled_functions_parse_class.phpt @@ -0,0 +1,22 @@ +--TEST-- +Disable functions - Parsing of an Object as a return value of a function +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_ret.ini +--FILE-- +a() instanceof StdClass)?'Y':'N'; +?> +--EXPECT-- +Y diff --git a/src/tests/disabled_functions_require.phpt b/src/tests/disabled_functions_require.phpt new file mode 100644 index 00000000..1eedde42 --- /dev/null +++ b/src/tests/disabled_functions_require.phpt @@ -0,0 +1,25 @@ +--TEST-- +Disable functions - Require +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_require.ini +--FILE-- + +--XFAIL-- +PHP doesn't replace the format string, so the test is failing. +--EXPECTF-- +[snuffleupagus][0.0.0.0][include][drop] Inclusion of a forbidden file (%a/test.bla) +--CLEAN-- + diff --git a/src/tests/disabled_functions_ret.phpt b/src/tests/disabled_functions_ret.phpt new file mode 100644 index 00000000..b64bf70a --- /dev/null +++ b/src/tests/disabled_functions_ret.phpt @@ -0,0 +1,13 @@ +--TEST-- +Disable functions check on `ret`. +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_ret.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/disabled_functions_ret.php:2, because the return value (0) of the function 'strpos' matched a rule. diff --git a/src/tests/disabled_functions_ret2.phpt b/src/tests/disabled_functions_ret2.phpt new file mode 100644 index 00000000..b713201d --- /dev/null +++ b/src/tests/disabled_functions_ret2.phpt @@ -0,0 +1,12 @@ +--TEST-- +Disable functions check on `ret`. +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_ret.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/disabled_functions_ret2.php:2, because the return value (0) of the function 'stripos' matched a rule. diff --git a/src/tests/disabled_functions_ret3.phpt b/src/tests/disabled_functions_ret3.phpt new file mode 100644 index 00000000..d5f96d03 --- /dev/null +++ b/src/tests/disabled_functions_ret3.phpt @@ -0,0 +1,22 @@ +--TEST-- +Disable functions check on `ret`. +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_ret.ini +--FILE-- +a() . ".\n"; +echo("We're at the end of the execution.\n"); +?> +--EXPECTF-- +We're in function `a`. +`a` returned: 1. +We're at the end of the execution. \ No newline at end of file diff --git a/src/tests/disabled_functions_ret_allow.phpt b/src/tests/disabled_functions_ret_allow.phpt new file mode 100644 index 00000000..16909955 --- /dev/null +++ b/src/tests/disabled_functions_ret_allow.phpt @@ -0,0 +1,13 @@ +--TEST-- +Disable functions check on `ret`. +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_ret_allow.ini +--FILE-- + +--EXPECT-- +00 \ No newline at end of file diff --git a/src/tests/disabled_functions_ret_allow_value.phpt b/src/tests/disabled_functions_ret_allow_value.phpt new file mode 100644 index 00000000..881a0069 --- /dev/null +++ b/src/tests/disabled_functions_ret_allow_value.phpt @@ -0,0 +1,12 @@ +--TEST-- +Disable functions check on `ret` allowed +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_ret_allow_value.ini +--FILE-- + +--EXPECT-- +0 diff --git a/src/tests/disabled_functions_ret_right_hash.phpt b/src/tests/disabled_functions_ret_right_hash.phpt new file mode 100644 index 00000000..e0d8b5b1 --- /dev/null +++ b/src/tests/disabled_functions_ret_right_hash.phpt @@ -0,0 +1,12 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_ret_right_hash.ini +--FILE-- + +--EXPECTF-- +1337 diff --git a/src/tests/disabled_functions_ret_simulation.phpt b/src/tests/disabled_functions_ret_simulation.phpt new file mode 100644 index 00000000..58af3a9c --- /dev/null +++ b/src/tests/disabled_functions_ret_simulation.phpt @@ -0,0 +1,18 @@ +--TEST-- +Disable functions check on `ret` simulation +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_ret_simulation.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][notice] The execution has been aborted in %a/disabled_functions_ret_simulation.php:2, because the return value (0) of the function 'strpos' matched a rule. +0 +[snuffleupagus][0.0.0.0][disabled_function][notice] The execution has been aborted in %a/disabled_functions_ret_simulation.php:3, because the function 'stripos' returned '0', which matched the rule '1'. +0 +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/disabled_functions_ret_simulation.php:4, because the return value (0) of the function 'strcmp' matched a rule. diff --git a/src/tests/disabled_functions_ret_type.phpt b/src/tests/disabled_functions_ret_type.phpt new file mode 100644 index 00000000..f1c6e4c9 --- /dev/null +++ b/src/tests/disabled_functions_ret_type.phpt @@ -0,0 +1,16 @@ +--TEST-- +Disable functions check on `ret` by type matching on boolean +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_ret_type.ini +--FILE-- + +--EXPECTF-- +0 +1337 +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/tests/disabled_functions_ret_type.php:%d, because the function 'strpos' returned 'FALSE', which matched the rule 'Return value is FALSE'. diff --git a/src/tests/disabled_functions_ret_type_double.phpt b/src/tests/disabled_functions_ret_type_double.phpt new file mode 100644 index 00000000..b7942e1b --- /dev/null +++ b/src/tests/disabled_functions_ret_type_double.phpt @@ -0,0 +1,12 @@ +--TEST-- +Disable functions check on `ret` by type matching (double). +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_ret_type_double.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/disabled_functions_ret_type_double.php:%d, because the function 'cos' returned '0.877583', which matched the rule 'Return value is a double'. diff --git a/src/tests/disabled_functions_ret_type_long.phpt b/src/tests/disabled_functions_ret_type_long.phpt new file mode 100644 index 00000000..b841c64c --- /dev/null +++ b/src/tests/disabled_functions_ret_type_long.phpt @@ -0,0 +1,12 @@ +--TEST-- +Disable functions check on `ret` by type matching (long). +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_ret_type_long.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/disabled_functions_ret_type_long.php:%d, because the function 'strlen' returned '5', which matched the rule 'Return value is a long'. diff --git a/src/tests/disabled_functions_ret_type_resource.phpt b/src/tests/disabled_functions_ret_type_resource.phpt new file mode 100644 index 00000000..4ceb6105 --- /dev/null +++ b/src/tests/disabled_functions_ret_type_resource.phpt @@ -0,0 +1,12 @@ +--TEST-- +Disable functions check on `ret` by type matching (resource). +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_ret_type_resource.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/disabled_functions_ret_type_resource.php:2, because the function 'fopen' returned 'RESOURCE', which matched the rule 'Return value is a resource'. diff --git a/src/tests/disabled_functions_ret_type_str.phpt b/src/tests/disabled_functions_ret_type_str.phpt new file mode 100644 index 00000000..8c48b1da --- /dev/null +++ b/src/tests/disabled_functions_ret_type_str.phpt @@ -0,0 +1,12 @@ +--TEST-- +Disable functions check on `ret` by type matching (string). +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_ret_type_str.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/disabled_functions_ret_type_str.php:%d, because the function 'substr' returned 'et', which matched the rule 'Return value is a string'. diff --git a/src/tests/disabled_functions_ret_type_true.phpt b/src/tests/disabled_functions_ret_type_true.phpt new file mode 100644 index 00000000..a5eae387 --- /dev/null +++ b/src/tests/disabled_functions_ret_type_true.phpt @@ -0,0 +1,16 @@ +--TEST-- +Disable functions check on `ret` by type matching (true). +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_ret_type_true.ini +--FILE-- + +--EXPECTF-- +bool(false) +1337 +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/disabled_functions_ret_type_true.php:%d, because the function 'is_numeric' returned 'TRUE', which matched the rule 'Return value is a true'. diff --git a/src/tests/disabled_functions_ret_val.phpt b/src/tests/disabled_functions_ret_val.phpt new file mode 100644 index 00000000..8a02b297 --- /dev/null +++ b/src/tests/disabled_functions_ret_val.phpt @@ -0,0 +1,14 @@ +--TEST-- +Disable functions ret val +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_retval.ini +--FILE-- + +--EXPECTF-- +fufu +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/disabled_functions_ret_val.php:3, because the return value (fufufu) of the function 'str_repeat' matched a rule. diff --git a/src/tests/disabled_functions_ret_val_rx.phpt b/src/tests/disabled_functions_ret_val_rx.phpt new file mode 100644 index 00000000..1054b706 --- /dev/null +++ b/src/tests/disabled_functions_ret_val_rx.phpt @@ -0,0 +1,14 @@ +--TEST-- +Disable functions ret val rx +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/disabled_functions_retval_rx.ini +--FILE-- + +--EXPECTF-- +fufu +[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in %a/disabled_functions_ret_val_rx.php:3, because the return value (fufufu) of the function 'str_repeat' matched a rule. diff --git a/src/tests/disabled_functions_right_hash.phpt b/src/tests/disabled_functions_right_hash.phpt new file mode 100644 index 00000000..f3c5fb31 --- /dev/null +++ b/src/tests/disabled_functions_right_hash.phpt @@ -0,0 +1,12 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_right_hash.ini +--FILE-- + +--EXPECTF-- +1337 diff --git a/src/tests/disabled_functions_runtime.phpt b/src/tests/disabled_functions_runtime.phpt new file mode 100644 index 00000000..1c6a1415 --- /dev/null +++ b/src/tests/disabled_functions_runtime.phpt @@ -0,0 +1,31 @@ +--TEST-- +Disable functions - runtime inclusion +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_functions_param_runtime.ini +--FILE-- + +--EXPECTF-- +1338 +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'test' in %a has been disabled, because its argument 'param' content (1337) matched a rule. +--CLEAN-- + diff --git a/src/tests/disabled_functions_zero_cidr.phpt b/src/tests/disabled_functions_zero_cidr.phpt new file mode 100644 index 00000000..35d187a9 --- /dev/null +++ b/src/tests/disabled_functions_zero_cidr.phpt @@ -0,0 +1,18 @@ +--TEST-- +Disable functions +--SKIPIF-- + +--ENV-- +return << +--EXPECTF-- +[snuffleupagus][127.0.0.1][disabled_function][drop] The call to the function 'system' in %a/tests/disabled_functions_zero_cidr.php:2 has been disabled. +1337 diff --git a/src/tests/disabled_option.phpt b/src/tests/disabled_option.phpt new file mode 100644 index 00000000..8bc7e399 --- /dev/null +++ b/src/tests/disabled_option.phpt @@ -0,0 +1,16 @@ +--TEST-- +Harden rand +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_rand_harden_disabled.ini +--FILE-- + +--EXPECT-- +84 +84 diff --git a/src/tests/disabled_user_functions.phpt b/src/tests/disabled_user_functions.phpt new file mode 100644 index 00000000..8952d43e --- /dev/null +++ b/src/tests/disabled_user_functions.phpt @@ -0,0 +1,15 @@ +--TEST-- +Disabled user-created functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_disabled_user_functions.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'my_super_function' in %a/tests/disabled_user_functions.php:3 has been disabled. diff --git a/src/tests/dump_request.phpt b/src/tests/dump_request.phpt new file mode 100644 index 00000000..a752def7 --- /dev/null +++ b/src/tests/dump_request.phpt @@ -0,0 +1,39 @@ +--TEST-- +Dump request +--SKIPIF-- + +--POST-- +post_a=data_post_a&post_b=data_post_b +--GET-- +get_a=data_get_a&get_b=data_get_b +--COOKIE-- +cookie_a=data_cookie_a&cookie_b=data_cookie_b +--INI-- +sp.configuration_file={PWD}/config/dump_request.ini +--FILE-- + +--EXPECTF-- +1 +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'system' in %a/dump_request.php:%d has been disabled. diff --git a/src/tests/dump_request_invalid_folder.phpt b/src/tests/dump_request_invalid_folder.phpt new file mode 100644 index 00000000..b866f700 --- /dev/null +++ b/src/tests/dump_request_invalid_folder.phpt @@ -0,0 +1,25 @@ +--TEST-- +Dump request - invalid folder. +--SKIPIF-- + +--POST-- +post_a=data_post_a&post_b=data_post_b +--GET-- +get_a=data_get_a&get_b=data_get_b +--COOKIE-- +cookie_a=data_cookie_a&cookie_b=data_cookie_b +--INI-- +sp.configuration_file={PWD}/config/dump_request_invalid_folder.ini +--FILE-- + +--EXPECTF-- +1 +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'system' in %atests/dump_request_invalid_folder.php:3 has been disabled. +[snuffleupagus][0.0.0.0][request_logging][error] Unable to open /root/NON_EXISTENT/FOLDER/PLEASE/sp_dump_%a_0.0.0.0.dump +2 \ No newline at end of file diff --git a/src/tests/dump_request_too_big.phpt b/src/tests/dump_request_too_big.phpt new file mode 100644 index 00000000..81eb71c7 --- /dev/null +++ b/src/tests/dump_request_too_big.phpt @@ -0,0 +1,42 @@ +--TEST-- +Dump request -- to big, so it's truncated. +--SKIPIF-- + +--POST-- +post_a=data_post_a&post_b=data_post_b&post_c=c +--GET-- +get_a=data_get_a&get_b=data_get_b&get_c=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaBBBB +--COOKIE-- +cookie_a=data_cookie_a&cookie_b=data_cookie_b&data_cookie_c=cookie_c +--ENV-- +return << +--EXPECTF-- +1 +[snuffleupagus][127.0.0.1][disabled_function][drop] The call to the function 'system' in %a/dump_request_too_big.php:%d has been disabled. diff --git a/src/tests/empty_conf.phpt b/src/tests/empty_conf.phpt new file mode 100644 index 00000000..411c817f --- /dev/null +++ b/src/tests/empty_conf.phpt @@ -0,0 +1,8 @@ +--TEST-- +Empty configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/empty_conf.ini +--FILE-- +--EXPECT-- diff --git a/src/tests/encrypt_cookies.phpt b/src/tests/encrypt_cookies.phpt new file mode 100644 index 00000000..f8bf64f7 --- /dev/null +++ b/src/tests/encrypt_cookies.phpt @@ -0,0 +1,22 @@ +--TEST-- +Cookie decryption in ipv4 +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_encrypted_cookies.ini +--COOKIE-- +super_cookie=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEmXkk3H0xheoOMxoWPEDw1Zd8NAmD9KbB2DSjQ=%3d;awful_cookie=awful_cookie_value; +--ENV-- +return << +--EXPECT-- +array(2) { + ["super_cookie"]=> + string(11) "super_value" + ["awful_cookie"]=> + string(18) "awful_cookie_value" +} diff --git a/src/tests/encrypt_cookies2.phpt b/src/tests/encrypt_cookies2.phpt new file mode 100644 index 00000000..be4c9903 --- /dev/null +++ b/src/tests/encrypt_cookies2.phpt @@ -0,0 +1,23 @@ +--TEST-- +Cookie encryption in ipv4 +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_encrypted_cookies.ini +--COOKIE-- +--ENV-- +return << +--EXPECT-- +array(0) { +} diff --git a/src/tests/encrypt_cookies3.phpt b/src/tests/encrypt_cookies3.phpt new file mode 100644 index 00000000..c85c5dca --- /dev/null +++ b/src/tests/encrypt_cookies3.phpt @@ -0,0 +1,23 @@ +--TEST-- +Cookie decryption with ipv6 +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_encrypted_cookies.ini +--COOKIE-- +super_cookie=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJNTUge7MpiVNi4q3DqstbcumllXBir0CbIQiDI%3D;awful_cookie=awful_cookie_value; +--ENV-- +return << +--EXPECT-- +array(2) { + ["super_cookie"]=> + string(11) "super_value" + ["awful_cookie"]=> + string(18) "awful_cookie_value" +} diff --git a/src/tests/encrypt_cookies4.phpt b/src/tests/encrypt_cookies4.phpt new file mode 100644 index 00000000..14d737ad --- /dev/null +++ b/src/tests/encrypt_cookies4.phpt @@ -0,0 +1,23 @@ +--TEST-- +Cookie encryption in ipv6 +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_encrypted_cookies.ini +--COOKIE-- +--ENV-- +return << +--EXPECT-- +array(0) { +} diff --git a/src/tests/encrypt_cookies_invalid_decryption.phpt b/src/tests/encrypt_cookies_invalid_decryption.phpt new file mode 100644 index 00000000..a5187c1c --- /dev/null +++ b/src/tests/encrypt_cookies_invalid_decryption.phpt @@ -0,0 +1,23 @@ +--TEST-- +Cookie encryption +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_encrypted_cookies.ini +display_errors=1 +display_startup_errors=1 +error_reporting=E_ALL +--COOKIE-- +super_cookie=jWjORGsgZyqzk3WA63XZBmUoSknXWnXDfAAAAAAAAAAAAAAAAAAAAAA7LiMDfkpP94jDnMVH%2Fm41GeL0Y00q3mbOFYz%2FS9mQGySu;awful_cookie=awful_cookie_value; +--ENV-- +return << +--EXPECT-- + +array(1) { + ["awful_cookie"]=> + string(18) "awful_cookie_value" +} diff --git a/src/tests/encrypt_cookies_invalid_decryption2.phpt b/src/tests/encrypt_cookies_invalid_decryption2.phpt new file mode 100644 index 00000000..f18cf6df --- /dev/null +++ b/src/tests/encrypt_cookies_invalid_decryption2.phpt @@ -0,0 +1,23 @@ +--TEST-- +Cookie encryption +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_encrypted_cookies.ini +display_errors=1 +display_startup_errors=1 +error_reporting=E_ALL +--COOKIE-- +super_cookie=1337;awful_cookie=awful_cookie_value; +--ENV-- +return << +--EXPECT-- + +array(1) { + ["awful_cookie"]=> + string(18) "awful_cookie_value" +} diff --git a/src/tests/encrypt_cookies_invalid_decryption3.phpt b/src/tests/encrypt_cookies_invalid_decryption3.phpt new file mode 100644 index 00000000..f4afc325 --- /dev/null +++ b/src/tests/encrypt_cookies_invalid_decryption3.phpt @@ -0,0 +1,21 @@ +--TEST-- +Cookie encryption +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_encrypted_cookies.ini +--COOKIE-- +super_cookie=;awful_cookie=awful_cookie_value; +--ENV-- +return << +--EXPECT-- +array(2) { + ["super_cookie"]=> + string(0) "" + ["awful_cookie"]=> + string(18) "awful_cookie_value" +} diff --git a/src/tests/encryption_key_only.phpt b/src/tests/encryption_key_only.phpt new file mode 100644 index 00000000..bf5edb58 --- /dev/null +++ b/src/tests/encryption_key_only.phpt @@ -0,0 +1,13 @@ +--TEST-- +Encryption key only +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/encryption_key_only.ini +--FILE-- + +--EXPECT-- +1337 + diff --git a/src/tests/example_configuration.phpt b/src/tests/example_configuration.phpt new file mode 100644 index 00000000..0bbf59c6 --- /dev/null +++ b/src/tests/example_configuration.phpt @@ -0,0 +1,12 @@ +--TEST-- +Shipped configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/../../config/examples.ini +--FILE-- + +--EXPECTF-- +0 diff --git a/src/tests/global_strict.phpt b/src/tests/global_strict.phpt new file mode 100644 index 00000000..e06721c5 --- /dev/null +++ b/src/tests/global_strict.phpt @@ -0,0 +1,16 @@ +--TEST-- +Global strict mode +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/global_strict.ini +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught TypeError: strcmp() expects parameter 2 to be string, array given in %a/global_strict.php:2 +Stack trace: +#0 %a/global_strict.php(2): strcmp('pouet', Array) +#1 {main} + thrown in %a/global_strict.php on line 2 diff --git a/src/tests/global_strict_disabled.phpt b/src/tests/global_strict_disabled.phpt new file mode 100644 index 00000000..ca3ddfa6 --- /dev/null +++ b/src/tests/global_strict_disabled.phpt @@ -0,0 +1,14 @@ +--TEST-- +Global strict mode +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/global_strict_disabled.ini +--FILE-- + +--EXPECTF-- +Warning: strcmp() expects parameter 2 to be string, array given in %a/global_strict_disabled.php on line 2 +1337 diff --git a/src/tests/harden_mt_rand.phpt b/src/tests/harden_mt_rand.phpt new file mode 100644 index 00000000..8887613a --- /dev/null +++ b/src/tests/harden_mt_rand.phpt @@ -0,0 +1,22 @@ +--TEST-- +Harden mt_rand +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/harden_rand.ini +--FILE-- + +--EXPECT-- +win diff --git a/src/tests/harden_rand.phpt b/src/tests/harden_rand.phpt new file mode 100644 index 00000000..391bccc3 --- /dev/null +++ b/src/tests/harden_rand.phpt @@ -0,0 +1,24 @@ +--TEST-- +Harden rand +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/harden_rand.ini +--FILE-- + +--EXPECT-- +win diff --git a/src/tests/harden_rand_noargs.phpt b/src/tests/harden_rand_noargs.phpt new file mode 100644 index 00000000..643a4538 --- /dev/null +++ b/src/tests/harden_rand_noargs.phpt @@ -0,0 +1,62 @@ +--TEST-- +Harden rand without any arguments +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/harden_rand.ini +We should fix this +--FILE-- + +--EXPECTF-- +Warning: rand() expects exactly 2 parameters, 1 given in %s/tests/harden_rand_noargs.php on line %d + +Warning: mt_rand() expects exactly 2 parameters, 1 given in %s/tests/harden_rand_noargs.php on line %d + +Warning: mt_rand(): max(1) is smaller than min(2) in %s/tests/harden_rand_noargs.php on line %d + +Warning: rand() expects exactly 2 parameters, 3 given in %s/tests/harden_rand_noargs.php on line %d + +Warning: mt_rand() expects exactly 2 parameters, 3 given in %s/tests/harden_rand_noargs.php on line %d + +Warning: rand() expects parameter 1 to be integer, string given in %s/tests/harden_rand_noargs.php on line %d + +Warning: mt_rand() expects parameter 1 to be integer, string given in %s/tests/harden_rand_noargs.php on line %d + +Warning: rand() expects parameter 2 to be integer, string given in %s/tests/harden_rand_noargs.php on line %d + +Warning: mt_rand() expects parameter 2 to be integer, string given in %s/tests/harden_rand_noargs.php on line %d + +Warning: rand() expects exactly 2 parameters, 3 given in %s/tests/harden_rand_noargs.php on line %d + +Warning: mt_rand() expects exactly 2 parameters, 3 given in %s/tests/harden_rand_noargs.php on line %d +Everything is fine +Absolutely everything +Even with single quotes diff --git a/src/tests/inexistent_conf_file.phpt b/src/tests/inexistent_conf_file.phpt new file mode 100644 index 00000000..c7c3fcdc --- /dev/null +++ b/src/tests/inexistent_conf_file.phpt @@ -0,0 +1,10 @@ +--TEST-- +Check for snuffleupagus presence +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/unexistent_configuration_file.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][config][error] Could not open configuration file %a/tests/config/unexistent_configuration_file.ini : No such file or directory diff --git a/src/tests/loading.phpt b/src/tests/loading.phpt new file mode 100644 index 00000000..25e2e170 --- /dev/null +++ b/src/tests/loading.phpt @@ -0,0 +1,10 @@ +--TEST-- +Check for snuffleupagus presence +--SKIPIF-- + +--FILE-- + +--EXPECT-- +snuffleupagus extension is available diff --git a/src/tests/noncore_function_hooking.phpt b/src/tests/noncore_function_hooking.phpt new file mode 100644 index 00000000..106123c0 --- /dev/null +++ b/src/tests/noncore_function_hooking.phpt @@ -0,0 +1,15 @@ +--TEST-- +Hooking of user-defined functions +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_noncore_function_hooking.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'custom_fun' in %a/tests/noncore_function_hooking.php:3 has been disabled. diff --git a/src/tests/phpinfo_presence.phpt b/src/tests/phpinfo_presence.phpt new file mode 100644 index 00000000..35ed0ed1 --- /dev/null +++ b/src/tests/phpinfo_presence.phpt @@ -0,0 +1,19 @@ +--TEST-- +Unserialize fail +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_serialize.ini +--FILE-- + +--EXPECT-- +1 diff --git a/src/tests/serialize.phpt b/src/tests/serialize.phpt new file mode 100644 index 00000000..e93dbaf8 --- /dev/null +++ b/src/tests/serialize.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test serialize hmac +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_serialize.ini +--FILE-- + +--EXPECT-- +s:1:"a";650609b417904d0d9bbf1fc44a975d13ecdf6b02b715c1a06271fb3b673f25b1 + diff --git a/src/tests/setcookie.phpt b/src/tests/setcookie.phpt new file mode 100644 index 00000000..ba1d1c1f --- /dev/null +++ b/src/tests/setcookie.phpt @@ -0,0 +1,35 @@ +--TEST-- +Set cookies. +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_encrypted_cookies.ini +--COOKIE-- +--ENV-- +return << +--EXPECTF-- +Warning: setcookie() expects at most 7 parameters, 8 given in %a/setcookie.php on line %d + +Warning: setcookie() expects at least 1 parameter, 0 given in %a/setcookie.php on line %d +1337 diff --git a/src/tests/shipped_configuration.phpt b/src/tests/shipped_configuration.phpt new file mode 100644 index 00000000..c060a852 --- /dev/null +++ b/src/tests/shipped_configuration.phpt @@ -0,0 +1,12 @@ +--TEST-- +Shipped configuration +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/../../config/default.ini +--FILE-- + +--EXPECTF-- +0 diff --git a/src/tests/unserialize.phpt b/src/tests/unserialize.phpt new file mode 100644 index 00000000..b1db9155 --- /dev/null +++ b/src/tests/unserialize.phpt @@ -0,0 +1,13 @@ +--TEST-- +Unserialize ok +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_serialize.ini +--FILE-- + +--EXPECT-- +string(1) "a" diff --git a/src/tests/unserialize_fail.phpt b/src/tests/unserialize_fail.phpt new file mode 100644 index 00000000..5c0bb807 --- /dev/null +++ b/src/tests/unserialize_fail.phpt @@ -0,0 +1,23 @@ +--TEST-- +Unserialize fail +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_serialize.ini +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][unserialize][drop] The serialized object is too small. +bool(false) +[snuffleupagus][0.0.0.0][unserialize][drop] Invalid HMAC for s:1:"a";alyualskdufyhalkdjsfh +NULL +[snuffleupagus][0.0.0.0][unserialize][drop] The serialized object is too small. +bool(false) + +Warning: unserialize() expects at most 2 parameters, 4 given in %a/tests/unserialize_fail.php on line %d +bool(false) \ No newline at end of file diff --git a/src/tests/unserialize_sim.phpt b/src/tests/unserialize_sim.phpt new file mode 100644 index 00000000..8ebf64d8 --- /dev/null +++ b/src/tests/unserialize_sim.phpt @@ -0,0 +1,17 @@ +--TEST-- +Unserialize ok +--SKIPIF-- + +--INI-- +sp.configuration_file={PWD}/config/config_serialize_sim.ini +--FILE-- + +--EXPECT-- +s:1:"a";650609b417904d0d9bbf1fc44a975d13ecdf6b02b715c1a06271fb3b673f25b1string(1) "a" +[snuffleupagus][0.0.0.0][unserialize][notice] Invalid HMAC for s:1:"a";alyualskdufyhalkdjsfh +string(1) "a" diff --git a/src/tests/upload_validation.phpt b/src/tests/upload_validation.phpt new file mode 100644 index 00000000..c802c162 --- /dev/null +++ b/src/tests/upload_validation.phpt @@ -0,0 +1,16 @@ +--TEST-- +Upload a file, validation ok, no simulation +--INI-- +file_uploads=1 +sp.configuration_file={PWD}/config/upload_validation.ini +--POST_RAW-- +Content-Type: multipart/form-data; boundary=blabla +--blabla +Content-Disposition: form-data; name="test"; filename="test.php" +--blabla-- +--FILE-- + +--EXPECTF-- +1 diff --git a/src/tests/upload_validation_invalid.phpt b/src/tests/upload_validation_invalid.phpt new file mode 100644 index 00000000..f8c993b9 --- /dev/null +++ b/src/tests/upload_validation_invalid.phpt @@ -0,0 +1,17 @@ +--TEST-- +Upload a file, invalid validation script +--INI-- +file_uploads=1 +sp.configuration_file={PWD}/config/upload_validation_invalid.ini +--POST_RAW-- +Content-Type: multipart/form-data; boundary=blabla +--blabla +Content-Disposition: form-data; name="test"; filename="test.php" +--blabla-- +--FILE-- + +--EXPECTF-- +[snuffleupagus][0.0.0.0][upload_validation][error] Could not call './tests/data/upload_invalid.sh' : Exec format error +[snuffleupagus][0.0.0.0][upload_valiation][drop] The upload of test.php on ? was rejected. diff --git a/src/tests/upload_validation_ko.phpt b/src/tests/upload_validation_ko.phpt new file mode 100644 index 00000000..cf4057ae --- /dev/null +++ b/src/tests/upload_validation_ko.phpt @@ -0,0 +1,14 @@ +--TEST-- +Upload a file, validation ko, no simulation +--INI-- +file_uploads=1 +sp.configuration_file={PWD}/config/upload_validation_ko.ini +output_buffering=off +--POST_RAW-- +Content-Type: multipart/form-data; boundary=blabla +--blabla +Content-Disposition: form-data; name="test"; filename="test.php" +--blabla-- +--FILE-- +--EXPECTF-- +[snuffleupagus][0.0.0.0][upload_valiation][drop] The upload of test.php on ? was rejected. diff --git a/src/tests/upload_validation_no_exec.phpt b/src/tests/upload_validation_no_exec.phpt new file mode 100644 index 00000000..90a58da5 --- /dev/null +++ b/src/tests/upload_validation_no_exec.phpt @@ -0,0 +1,32 @@ +--TEST-- +Upload a file, validation script not executable +--INI-- +file_uploads=1 +sp.configuration_file={PWD}/config/upload_validation_non_exec.ini +output_buffering=off +--POST_RAW-- +Content-Type: multipart/form-data; boundary=blabla +--blabla +Content-Disposition: form-data; name="test"; filename="test.php" +--blabla-- +--FILE-- + +--EXPECTF-- +array(1) { + ["test"]=> + array(5) { + ["name"]=> + string(8) "test.php" + ["type"]=> + string(0) "" + ["tmp_name"]=> + string(0) "" + ["error"]=> + int(3) + ["size"]=> + int(0) + } +} diff --git a/src/tests/upload_validation_nocrash.phpt b/src/tests/upload_validation_nocrash.phpt new file mode 100644 index 00000000..6fa50d0a --- /dev/null +++ b/src/tests/upload_validation_nocrash.phpt @@ -0,0 +1,12 @@ +--TEST-- +Upload validation isn't crashing +--INI-- +file_uploads=1 +sp.configuration_file={PWD}/config/upload_validation_ok.ini +output_buffering=off +--FILE-- + +--EXPECTF-- +1 diff --git a/src/tests/upload_validation_ok.phpt b/src/tests/upload_validation_ok.phpt new file mode 100644 index 00000000..f9b50150 --- /dev/null +++ b/src/tests/upload_validation_ok.phpt @@ -0,0 +1,17 @@ +--TEST-- +Upload a file, validation ok, no simulation +--INI-- +file_uploads=1 +sp.configuration_file={PWD}/config/upload_validation_ok.ini +output_buffering=off +--POST_RAW-- +Content-Type: multipart/form-data; boundary=blabla +--blabla +Content-Disposition: form-data; name="test"; filename="test.php" +--blabla-- +--FILE-- + +--EXPECTF-- +1 diff --git a/src/tweetnacl.c b/src/tweetnacl.c new file mode 100644 index 00000000..937e879e --- /dev/null +++ b/src/tweetnacl.c @@ -0,0 +1,842 @@ +#include "tweetnacl.h" +#define FOR(i,n) for (i = 0;i < n;++i) +#define sv static void + +typedef unsigned char u8; +typedef unsigned long u32; +typedef unsigned long long u64; +typedef long long i64; +typedef i64 gf[16]; + + +/* it's really stupid that there isn't a syscall for this */ + +static int fd = -1; + +void randombytes(unsigned char *x,unsigned long long xlen) +{ + int i; + + if (fd == -1) { + for (;;) { + fd = open("/dev/urandom",O_RDONLY); + if (fd != -1) break; + sleep(1); + } + } + + while (xlen > 0) { + if (xlen < 1048576) i = xlen; else i = 1048576; + + i = read(fd,x,i); + if (i < 1) { + sleep(1); + continue; + } + + x += i; + xlen -= i; + } +} + + +static const u8 + _0[16], + _9[32] = {9}; +static const gf + gf0, + gf1 = {1}, + _121665 = {0xDB41,1}, + D = {0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203}, + D2 = {0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406}, + X = {0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169}, + Y = {0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666}, + I = {0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83}; + +static u32 L32(u32 x,int c) { return (x << c) | ((x&0xffffffff) >> (32 - c)); } + +static u32 ld32(const u8 *x) +{ + u32 u = x[3]; + u = (u<<8)|x[2]; + u = (u<<8)|x[1]; + return (u<<8)|x[0]; +} + +static u64 dl64(const u8 *x) +{ + u64 i,u=0; + FOR(i,8) u=(u<<8)|x[i]; + return u; +} + +sv st32(u8 *x,u32 u) +{ + int i; + FOR(i,4) { x[i] = u; u >>= 8; } +} + +sv ts64(u8 *x,u64 u) +{ + int i; + for (i = 7;i >= 0;--i) { x[i] = u; u >>= 8; } +} + +static int vn(const u8 *x,const u8 *y,int n) +{ + int i = 0; + u32 d = 0; + FOR(i,n) d |= x[i]^y[i]; + return (1 & ((d - 1) >> 8)) - 1; +} + +int crypto_verify_16(const u8 *x,const u8 *y) +{ + return vn(x,y,16); +} + +int crypto_verify_32(const u8 *x,const u8 *y) +{ + return vn(x,y,32); +} + +sv core(u8 *out,const u8 *in,const u8 *k,const u8 *c,int h) +{ + u32 w[16],x[16],y[16],t[4]; + int i,j,m; + + FOR(i,4) { + x[5*i] = ld32(c+4*i); + x[1+i] = ld32(k+4*i); + x[6+i] = ld32(in+4*i); + x[11+i] = ld32(k+16+4*i); + } + + FOR(i,16) y[i] = x[i]; + + FOR(i,20) { + FOR(j,4) { + FOR(m,4) t[m] = x[(5*j+4*m)%16]; + t[1] ^= L32(t[0]+t[3], 7); + t[2] ^= L32(t[1]+t[0], 9); + t[3] ^= L32(t[2]+t[1],13); + t[0] ^= L32(t[3]+t[2],18); + FOR(m,4) w[4*j+(j+m)%4] = t[m]; + } + FOR(m,16) x[m] = w[m]; + } + + if (h) { + FOR(i,16) x[i] += y[i]; + FOR(i,4) { + x[5*i] -= ld32(c+4*i); + x[6+i] -= ld32(in+4*i); + } + FOR(i,4) { + st32(out+4*i,x[5*i]); + st32(out+16+4*i,x[6+i]); + } + } else + FOR(i,16) st32(out + 4 * i,x[i] + y[i]); +} + +int crypto_core_salsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c) +{ + core(out,in,k,c,0); + return 0; +} + +int crypto_core_hsalsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c) +{ + core(out,in,k,c,1); + return 0; +} + +static const u8 sigma[16] = "expand 32-byte k"; + +int crypto_stream_salsa20_xor(u8 *c,const u8 *m,u64 b,const u8 *n,const u8 *k) +{ + u8 z[16],x[64]; + u32 u,i; + if (!b) return 0; + FOR(i,16) z[i] = 0; + FOR(i,8) z[i] = n[i]; + while (b >= 64) { + crypto_core_salsa20(x,z,k,sigma); + FOR(i,64) c[i] = (m?m[i]:0) ^ x[i]; + u = 1; + for (i = 8;i < 16;++i) { + u += (u32) z[i]; + z[i] = u; + u >>= 8; + } + b -= 64; + c += 64; + if (m) m += 64; + } + if (b) { + crypto_core_salsa20(x,z,k,sigma); + FOR(i,b) c[i] = (m?m[i]:0) ^ x[i]; + } + return 0; +} + +int crypto_stream_salsa20(u8 *c,u64 d,const u8 *n,const u8 *k) +{ + return crypto_stream_salsa20_xor(c,0,d,n,k); +} + +int crypto_stream(u8 *c,u64 d,const u8 *n,const u8 *k) +{ + u8 s[32]; + crypto_core_hsalsa20(s,n,k,sigma); + return crypto_stream_salsa20(c,d,n+16,s); +} + +int crypto_stream_xor(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) +{ + u8 s[32]; + crypto_core_hsalsa20(s,n,k,sigma); + return crypto_stream_salsa20_xor(c,m,d,n+16,s); +} + +sv add1305(u32 *h,const u32 *c) +{ + u32 j,u = 0; + FOR(j,17) { + u += h[j] + c[j]; + h[j] = u & 255; + u >>= 8; + } +} + +static const u32 minusp[17] = { + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252 +} ; + +int crypto_onetimeauth(u8 *out,const u8 *m,u64 n,const u8 *k) +{ + u32 s,i,j,u,x[17],r[17],h[17],c[17],g[17]; + + FOR(j,17) r[j]=h[j]=0; + FOR(j,16) r[j]=k[j]; + r[3]&=15; + r[4]&=252; + r[7]&=15; + r[8]&=252; + r[11]&=15; + r[12]&=252; + r[15]&=15; + + while (n > 0) { + FOR(j,17) c[j] = 0; + for (j = 0;(j < 16) && (j < n);++j) c[j] = m[j]; + c[j] = 1; + m += j; n -= j; + add1305(h,c); + FOR(i,17) { + x[i] = 0; + FOR(j,17) x[i] += h[j] * ((j <= i) ? r[i - j] : 320 * r[i + 17 - j]); + } + FOR(i,17) h[i] = x[i]; + u = 0; + FOR(j,16) { + u += h[j]; + h[j] = u & 255; + u >>= 8; + } + u += h[16]; h[16] = u & 3; + u = 5 * (u >> 2); + FOR(j,16) { + u += h[j]; + h[j] = u & 255; + u >>= 8; + } + u += h[16]; h[16] = u; + } + + FOR(j,17) g[j] = h[j]; + add1305(h,minusp); + s = -(h[16] >> 7); + FOR(j,17) h[j] ^= s & (g[j] ^ h[j]); + + FOR(j,16) c[j] = k[j + 16]; + c[16] = 0; + add1305(h,c); + FOR(j,16) out[j] = h[j]; + return 0; +} + +int crypto_onetimeauth_verify(const u8 *h,const u8 *m,u64 n,const u8 *k) +{ + u8 x[16]; + crypto_onetimeauth(x,m,n,k); + return crypto_verify_16(h,x); +} + +int crypto_secretbox(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) +{ + int i; + if (d < 32) return -1; + crypto_stream_xor(c,m,d,n,k); + crypto_onetimeauth(c + 16,c + 32,d - 32,c); + FOR(i,16) c[i] = 0; + return 0; +} + +int crypto_secretbox_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k) +{ + int i; + u8 x[32]; + if (d < 32) return -1; + crypto_stream(x,32,n,k); + if (crypto_onetimeauth_verify(c + 16,c + 32,d - 32,x) != 0) return -1; + crypto_stream_xor(m,c,d,n,k); + FOR(i,32) m[i] = 0; + return 0; +} + +sv set25519(gf r, const gf a) +{ + int i; + FOR(i,16) r[i]=a[i]; +} + +sv car25519(gf o) +{ + int i; + i64 c; + FOR(i,16) { + o[i]+=(1LL<<16); + c=o[i]>>16; + o[(i+1)*(i<15)]+=c-1+37*(c-1)*(i==15); + o[i]-=c<<16; + } +} + +sv sel25519(gf p,gf q,int b) +{ + i64 t,i,c=~(b-1); + FOR(i,16) { + t= c&(p[i]^q[i]); + p[i]^=t; + q[i]^=t; + } +} + +sv pack25519(u8 *o,const gf n) +{ + int i,j,b; + gf m,t; + FOR(i,16) t[i]=n[i]; + car25519(t); + car25519(t); + car25519(t); + FOR(j,2) { + m[0]=t[0]-0xffed; + for(i=1;i<15;i++) { + m[i]=t[i]-0xffff-((m[i-1]>>16)&1); + m[i-1]&=0xffff; + } + m[15]=t[15]-0x7fff-((m[14]>>16)&1); + b=(m[15]>>16)&1; + m[14]&=0xffff; + sel25519(t,m,1-b); + } + FOR(i,16) { + o[2*i]=t[i]&0xff; + o[2*i+1]=t[i]>>8; + } +} + +static int neq25519(const gf a, const gf b) +{ + u8 c[32],d[32]; + pack25519(c,a); + pack25519(d,b); + return crypto_verify_32(c,d); +} + +static u8 par25519(const gf a) +{ + u8 d[32]; + pack25519(d,a); + return d[0]&1; +} + +sv unpack25519(gf o, const u8 *n) +{ + int i; + FOR(i,16) o[i]=n[2*i]+((i64)n[2*i+1]<<8); + o[15]&=0x7fff; +} + +sv A(gf o,const gf a,const gf b) +{ + int i; + FOR(i,16) o[i]=a[i]+b[i]; +} + +sv Z(gf o,const gf a,const gf b) +{ + int i; + FOR(i,16) o[i]=a[i]-b[i]; +} + +sv M(gf o,const gf a,const gf b) +{ + i64 i,j,t[31]; + FOR(i,31) t[i]=0; + FOR(i,16) FOR(j,16) t[i+j]+=a[i]*b[j]; + FOR(i,15) t[i]+=38*t[i+16]; + FOR(i,16) o[i]=t[i]; + car25519(o); + car25519(o); +} + +sv S(gf o,const gf a) +{ + M(o,a,a); +} + +sv inv25519(gf o,const gf i) +{ + gf c; + int a; + FOR(a,16) c[a]=i[a]; + for(a=253;a>=0;a--) { + S(c,c); + if(a!=2&&a!=4) M(c,c,i); + } + FOR(a,16) o[a]=c[a]; +} + +sv pow2523(gf o,const gf i) +{ + gf c; + int a; + FOR(a,16) c[a]=i[a]; + for(a=250;a>=0;a--) { + S(c,c); + if(a!=1) M(c,c,i); + } + FOR(a,16) o[a]=c[a]; +} + +int crypto_scalarmult(u8 *q,const u8 *n,const u8 *p) +{ + u8 z[32]; + i64 x[80],r,i; + gf a,b,c,d,e,f; + FOR(i,31) z[i]=n[i]; + z[31]=(n[31]&127)|64; + z[0]&=248; + unpack25519(x,p); + FOR(i,16) { + b[i]=x[i]; + d[i]=a[i]=c[i]=0; + } + a[0]=d[0]=1; + for(i=254;i>=0;--i) { + r=(z[i>>3]>>(i&7))&1; + sel25519(a,b,r); + sel25519(c,d,r); + A(e,a,c); + Z(a,a,c); + A(c,b,d); + Z(b,b,d); + S(d,e); + S(f,a); + M(a,c,a); + M(c,b,e); + A(e,a,c); + Z(a,a,c); + S(b,a); + Z(c,d,f); + M(a,c,_121665); + A(a,a,d); + M(c,c,a); + M(a,d,f); + M(d,b,x); + S(b,e); + sel25519(a,b,r); + sel25519(c,d,r); + } + FOR(i,16) { + x[i+16]=a[i]; + x[i+32]=c[i]; + x[i+48]=b[i]; + x[i+64]=d[i]; + } + inv25519(x+32,x+32); + M(x+16,x+16,x+32); + pack25519(q,x+16); + return 0; +} + +int crypto_scalarmult_base(u8 *q,const u8 *n) +{ + return crypto_scalarmult(q,n,_9); +} + +int crypto_box_keypair(u8 *y,u8 *x) +{ + randombytes(x,32); + return crypto_scalarmult_base(y,x); +} + +int crypto_box_beforenm(u8 *k,const u8 *y,const u8 *x) +{ + u8 s[32]; + crypto_scalarmult(s,x,y); + return crypto_core_hsalsa20(k,_0,s,sigma); +} + +int crypto_box_afternm(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k) +{ + return crypto_secretbox(c,m,d,n,k); +} + +int crypto_box_open_afternm(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k) +{ + return crypto_secretbox_open(m,c,d,n,k); +} + +int crypto_box(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *y,const u8 *x) +{ + u8 k[32]; + crypto_box_beforenm(k,y,x); + return crypto_box_afternm(c,m,d,n,k); +} + +int crypto_box_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *y,const u8 *x) +{ + u8 k[32]; + crypto_box_beforenm(k,y,x); + return crypto_box_open_afternm(m,c,d,n,k); +} + +static u64 R(u64 x,int c) { return (x >> c) | (x << (64 - c)); } +static u64 Ch(u64 x,u64 y,u64 z) { return (x & y) ^ (~x & z); } +static u64 Maj(u64 x,u64 y,u64 z) { return (x & y) ^ (x & z) ^ (y & z); } +static u64 Sigma0(u64 x) { return R(x,28) ^ R(x,34) ^ R(x,39); } +static u64 Sigma1(u64 x) { return R(x,14) ^ R(x,18) ^ R(x,41); } +static u64 sigma0(u64 x) { return R(x, 1) ^ R(x, 8) ^ (x >> 7); } +static u64 sigma1(u64 x) { return R(x,19) ^ R(x,61) ^ (x >> 6); } + +static const u64 K[80] = +{ + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +int crypto_hashblocks(u8 *x,const u8 *m,u64 n) +{ + u64 z[8],b[8],a[8],w[16],t; + int i,j; + + FOR(i,8) z[i] = a[i] = dl64(x + 8 * i); + + while (n >= 128) { + FOR(i,16) w[i] = dl64(m + 8 * i); + + FOR(i,80) { + FOR(j,8) b[j] = a[j]; + t = a[7] + Sigma1(a[4]) + Ch(a[4],a[5],a[6]) + K[i] + w[i%16]; + b[7] = t + Sigma0(a[0]) + Maj(a[0],a[1],a[2]); + b[3] += t; + FOR(j,8) a[(j+1)%8] = b[j]; + if (i%16 == 15) + FOR(j,16) + w[j] += w[(j+9)%16] + sigma0(w[(j+1)%16]) + sigma1(w[(j+14)%16]); + } + + FOR(i,8) { a[i] += z[i]; z[i] = a[i]; } + + m += 128; + n -= 128; + } + + FOR(i,8) ts64(x+8*i,z[i]); + + return n; +} + +static const u8 iv[64] = { + 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08, + 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b, + 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b, + 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1, + 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1, + 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f, + 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b, + 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79 +} ; + +int crypto_hash(u8 *out,const u8 *m,u64 n) +{ + u8 h[64],x[256]; + u64 i,b = n; + + FOR(i,64) h[i] = iv[i]; + + crypto_hashblocks(h,m,n); + m += n; + n &= 127; + m -= n; + + FOR(i,256) x[i] = 0; + FOR(i,n) x[i] = m[i]; + x[n] = 128; + + n = 256-128*(n<112); + x[n-9] = b >> 61; + ts64(x+n-8,b<<3); + crypto_hashblocks(h,x,n); + + FOR(i,64) out[i] = h[i]; + + return 0; +} + +sv add(gf p[4],gf q[4]) +{ + gf a,b,c,d,t,e,f,g,h; + + Z(a, p[1], p[0]); + Z(t, q[1], q[0]); + M(a, a, t); + A(b, p[0], p[1]); + A(t, q[0], q[1]); + M(b, b, t); + M(c, p[3], q[3]); + M(c, c, D2); + M(d, p[2], q[2]); + A(d, d, d); + Z(e, b, a); + Z(f, d, c); + A(g, d, c); + A(h, b, a); + + M(p[0], e, f); + M(p[1], h, g); + M(p[2], g, f); + M(p[3], e, h); +} + +sv cswap(gf p[4],gf q[4],u8 b) +{ + int i; + FOR(i,4) + sel25519(p[i],q[i],b); +} + +sv pack(u8 *r,gf p[4]) +{ + gf tx, ty, zi; + inv25519(zi, p[2]); + M(tx, p[0], zi); + M(ty, p[1], zi); + pack25519(r, ty); + r[31] ^= par25519(tx) << 7; +} + +sv scalarmult(gf p[4],gf q[4],const u8 *s) +{ + int i; + set25519(p[0],gf0); + set25519(p[1],gf1); + set25519(p[2],gf1); + set25519(p[3],gf0); + for (i = 255;i >= 0;--i) { + u8 b = (s[i/8]>>(i&7))&1; + cswap(p,q,b); + add(q,p); + add(p,p); + cswap(p,q,b); + } +} + +sv scalarbase(gf p[4],const u8 *s) +{ + gf q[4]; + set25519(q[0],X); + set25519(q[1],Y); + set25519(q[2],gf1); + M(q[3],X,Y); + scalarmult(p,q,s); +} + +int crypto_sign_keypair(u8 *pk, u8 *sk) +{ + u8 d[64]; + gf p[4]; + int i; + + randombytes(sk, 32); + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + scalarbase(p,d); + pack(pk,p); + + FOR(i,32) sk[32 + i] = pk[i]; + return 0; +} + +static const u64 L[32] = {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10}; + +sv modL(u8 *r,i64 x[64]) +{ + i64 carry,i,j; + for (i = 63;i >= 32;--i) { + carry = 0; + for (j = i - 32;j < i - 12;++j) { + x[j] += carry - 16 * x[i] * L[j - (i - 32)]; + carry = (x[j] + 128) >> 8; + x[j] -= carry << 8; + } + x[j] += carry; + x[i] = 0; + } + carry = 0; + FOR(j,32) { + x[j] += carry - (x[31] >> 4) * L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + FOR(j,32) x[j] -= carry * L[j]; + FOR(i,32) { + x[i+1] += x[i] >> 8; + r[i] = x[i] & 255; + } +} + +sv reduce(u8 *r) +{ + i64 x[64],i; + FOR(i,64) x[i] = (u64) r[i]; + FOR(i,64) r[i] = 0; + modL(r,x); +} + +int crypto_sign(u8 *sm,u64 *smlen,const u8 *m,u64 n,const u8 *sk) +{ + u8 d[64],h[64],r[64]; + u64 i; + i64 j,x[64]; + gf p[4]; + + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + + *smlen = n+64; + FOR(i,n) sm[64 + i] = m[i]; + FOR(i,32) sm[32 + i] = d[32 + i]; + + crypto_hash(r, sm+32, n+32); + reduce(r); + scalarbase(p,r); + pack(sm,p); + + FOR(i,32) sm[i+32] = sk[i+32]; + crypto_hash(h,sm,n + 64); + reduce(h); + + FOR(i,64) x[i] = 0; + FOR(i,32) x[i] = (u64) r[i]; + FOR(i,32) FOR(j,32) x[i+j] += h[i] * (u64) d[j]; + modL(sm + 32,x); + + return 0; +} + +static int unpackneg(gf r[4],const u8 p[32]) +{ + gf t, chk, num, den, den2, den4, den6; + set25519(r[2],gf1); + unpack25519(r[1],p); + S(num,r[1]); + M(den,num,D); + Z(num,num,r[2]); + A(den,r[2],den); + + S(den2,den); + S(den4,den2); + M(den6,den4,den2); + M(t,den6,num); + M(t,t,den); + + pow2523(t,t); + M(t,t,num); + M(t,t,den); + M(t,t,den); + M(r[0],t,den); + + S(chk,r[0]); + M(chk,chk,den); + if (neq25519(chk, num)) M(r[0],r[0],I); + + S(chk,r[0]); + M(chk,chk,den); + if (neq25519(chk, num)) return -1; + + if (par25519(r[0]) == (p[31]>>7)) Z(r[0],gf0,r[0]); + + M(r[3],r[0],r[1]); + return 0; +} + +int crypto_sign_open(u8 *m,u64 *mlen,const u8 *sm,u64 n,const u8 *pk) +{ + u64 i; + u8 t[32],h[64]; + gf p[4],q[4]; + + *mlen = -1; + if (n < 64) return -1; + + if (unpackneg(q,pk)) return -1; + + FOR(i,n) m[i] = sm[i]; + FOR(i,32) m[i+32] = pk[i]; + crypto_hash(h,m,n); + reduce(h); + scalarmult(p,q,h); + + scalarbase(q,sm + 32); + add(p,q); + pack(t,p); + + n -= 64; + if (crypto_verify_32(sm, t)) { + FOR(i,n) m[i] = 0; + return -1; + } + + FOR(i,n) m[i] = sm[i + 64]; + *mlen = n; + return 0; +} diff --git a/src/tweetnacl.h b/src/tweetnacl.h new file mode 100644 index 00000000..508876d8 --- /dev/null +++ b/src/tweetnacl.h @@ -0,0 +1,277 @@ +#include +#include +#include +#include + +#ifndef TWEETNACL_H +#define TWEETNACL_H +#define crypto_auth_PRIMITIVE "hmacsha512256" +#define crypto_auth crypto_auth_hmacsha512256 +#define crypto_auth_verify crypto_auth_hmacsha512256_verify +#define crypto_auth_BYTES crypto_auth_hmacsha512256_BYTES +#define crypto_auth_KEYBYTES crypto_auth_hmacsha512256_KEYBYTES +#define crypto_auth_IMPLEMENTATION crypto_auth_hmacsha512256_IMPLEMENTATION +#define crypto_auth_VERSION crypto_auth_hmacsha512256_VERSION +#define crypto_auth_hmacsha512256_tweet_BYTES 32 +#define crypto_auth_hmacsha512256_tweet_KEYBYTES 32 +extern int crypto_auth_hmacsha512256_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_auth_hmacsha512256_tweet_verify(const unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); +#define crypto_auth_hmacsha512256_tweet_VERSION "-" +#define crypto_auth_hmacsha512256 crypto_auth_hmacsha512256_tweet +#define crypto_auth_hmacsha512256_verify crypto_auth_hmacsha512256_tweet_verify +#define crypto_auth_hmacsha512256_BYTES crypto_auth_hmacsha512256_tweet_BYTES +#define crypto_auth_hmacsha512256_KEYBYTES crypto_auth_hmacsha512256_tweet_KEYBYTES +#define crypto_auth_hmacsha512256_VERSION crypto_auth_hmacsha512256_tweet_VERSION +#define crypto_auth_hmacsha512256_IMPLEMENTATION "crypto_auth/hmacsha512256/tweet" +#define crypto_box_PRIMITIVE "curve25519xsalsa20poly1305" +#define crypto_box crypto_box_curve25519xsalsa20poly1305 +#define crypto_box_open crypto_box_curve25519xsalsa20poly1305_open +#define crypto_box_keypair crypto_box_curve25519xsalsa20poly1305_keypair +#define crypto_box_beforenm crypto_box_curve25519xsalsa20poly1305_beforenm +#define crypto_box_afternm crypto_box_curve25519xsalsa20poly1305_afternm +#define crypto_box_open_afternm crypto_box_curve25519xsalsa20poly1305_open_afternm +#define crypto_box_PUBLICKEYBYTES crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES +#define crypto_box_SECRETKEYBYTES crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES +#define crypto_box_BEFORENMBYTES crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES +#define crypto_box_NONCEBYTES crypto_box_curve25519xsalsa20poly1305_NONCEBYTES +#define crypto_box_ZEROBYTES crypto_box_curve25519xsalsa20poly1305_ZEROBYTES +#define crypto_box_BOXZEROBYTES crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES +#define crypto_box_IMPLEMENTATION crypto_box_curve25519xsalsa20poly1305_IMPLEMENTATION +#define crypto_box_VERSION crypto_box_curve25519xsalsa20poly1305_VERSION +#define crypto_box_curve25519xsalsa20poly1305_tweet_PUBLICKEYBYTES 32 +#define crypto_box_curve25519xsalsa20poly1305_tweet_SECRETKEYBYTES 32 +#define crypto_box_curve25519xsalsa20poly1305_tweet_BEFORENMBYTES 32 +#define crypto_box_curve25519xsalsa20poly1305_tweet_NONCEBYTES 24 +#define crypto_box_curve25519xsalsa20poly1305_tweet_ZEROBYTES 32 +#define crypto_box_curve25519xsalsa20poly1305_tweet_BOXZEROBYTES 16 +extern int crypto_box_curve25519xsalsa20poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *,const unsigned char *); +extern int crypto_box_curve25519xsalsa20poly1305_tweet_open(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *,const unsigned char *); +extern int crypto_box_curve25519xsalsa20poly1305_tweet_keypair(unsigned char *,unsigned char *); +extern int crypto_box_curve25519xsalsa20poly1305_tweet_beforenm(unsigned char *,const unsigned char *,const unsigned char *); +extern int crypto_box_curve25519xsalsa20poly1305_tweet_afternm(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +extern int crypto_box_curve25519xsalsa20poly1305_tweet_open_afternm(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +#define crypto_box_curve25519xsalsa20poly1305_tweet_VERSION "-" +#define crypto_box_curve25519xsalsa20poly1305 crypto_box_curve25519xsalsa20poly1305_tweet +#define crypto_box_curve25519xsalsa20poly1305_open crypto_box_curve25519xsalsa20poly1305_tweet_open +#define crypto_box_curve25519xsalsa20poly1305_keypair crypto_box_curve25519xsalsa20poly1305_tweet_keypair +#define crypto_box_curve25519xsalsa20poly1305_beforenm crypto_box_curve25519xsalsa20poly1305_tweet_beforenm +#define crypto_box_curve25519xsalsa20poly1305_afternm crypto_box_curve25519xsalsa20poly1305_tweet_afternm +#define crypto_box_curve25519xsalsa20poly1305_open_afternm crypto_box_curve25519xsalsa20poly1305_tweet_open_afternm +#define crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES crypto_box_curve25519xsalsa20poly1305_tweet_PUBLICKEYBYTES +#define crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES crypto_box_curve25519xsalsa20poly1305_tweet_SECRETKEYBYTES +#define crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES crypto_box_curve25519xsalsa20poly1305_tweet_BEFORENMBYTES +#define crypto_box_curve25519xsalsa20poly1305_NONCEBYTES crypto_box_curve25519xsalsa20poly1305_tweet_NONCEBYTES +#define crypto_box_curve25519xsalsa20poly1305_ZEROBYTES crypto_box_curve25519xsalsa20poly1305_tweet_ZEROBYTES +#define crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES crypto_box_curve25519xsalsa20poly1305_tweet_BOXZEROBYTES +#define crypto_box_curve25519xsalsa20poly1305_VERSION crypto_box_curve25519xsalsa20poly1305_tweet_VERSION +#define crypto_box_curve25519xsalsa20poly1305_IMPLEMENTATION "crypto_box/curve25519xsalsa20poly1305/tweet" +#define crypto_core_PRIMITIVE "salsa20" +#define crypto_core crypto_core_salsa20 +#define crypto_core_OUTPUTBYTES crypto_core_salsa20_OUTPUTBYTES +#define crypto_core_INPUTBYTES crypto_core_salsa20_INPUTBYTES +#define crypto_core_KEYBYTES crypto_core_salsa20_KEYBYTES +#define crypto_core_CONSTBYTES crypto_core_salsa20_CONSTBYTES +#define crypto_core_IMPLEMENTATION crypto_core_salsa20_IMPLEMENTATION +#define crypto_core_VERSION crypto_core_salsa20_VERSION +#define crypto_core_salsa20_tweet_OUTPUTBYTES 64 +#define crypto_core_salsa20_tweet_INPUTBYTES 16 +#define crypto_core_salsa20_tweet_KEYBYTES 32 +#define crypto_core_salsa20_tweet_CONSTBYTES 16 +extern int crypto_core_salsa20_tweet(unsigned char *,const unsigned char *,const unsigned char *,const unsigned char *); +#define crypto_core_salsa20_tweet_VERSION "-" +#define crypto_core_salsa20 crypto_core_salsa20_tweet +#define crypto_core_salsa20_OUTPUTBYTES crypto_core_salsa20_tweet_OUTPUTBYTES +#define crypto_core_salsa20_INPUTBYTES crypto_core_salsa20_tweet_INPUTBYTES +#define crypto_core_salsa20_KEYBYTES crypto_core_salsa20_tweet_KEYBYTES +#define crypto_core_salsa20_CONSTBYTES crypto_core_salsa20_tweet_CONSTBYTES +#define crypto_core_salsa20_VERSION crypto_core_salsa20_tweet_VERSION +#define crypto_core_salsa20_IMPLEMENTATION "crypto_core/salsa20/tweet" +#define crypto_core_hsalsa20_tweet_OUTPUTBYTES 32 +#define crypto_core_hsalsa20_tweet_INPUTBYTES 16 +#define crypto_core_hsalsa20_tweet_KEYBYTES 32 +#define crypto_core_hsalsa20_tweet_CONSTBYTES 16 +extern int crypto_core_hsalsa20_tweet(unsigned char *,const unsigned char *,const unsigned char *,const unsigned char *); +#define crypto_core_hsalsa20_tweet_VERSION "-" +#define crypto_core_hsalsa20 crypto_core_hsalsa20_tweet +#define crypto_core_hsalsa20_OUTPUTBYTES crypto_core_hsalsa20_tweet_OUTPUTBYTES +#define crypto_core_hsalsa20_INPUTBYTES crypto_core_hsalsa20_tweet_INPUTBYTES +#define crypto_core_hsalsa20_KEYBYTES crypto_core_hsalsa20_tweet_KEYBYTES +#define crypto_core_hsalsa20_CONSTBYTES crypto_core_hsalsa20_tweet_CONSTBYTES +#define crypto_core_hsalsa20_VERSION crypto_core_hsalsa20_tweet_VERSION +#define crypto_core_hsalsa20_IMPLEMENTATION "crypto_core/hsalsa20/tweet" +#define crypto_hashblocks_PRIMITIVE "sha512" +#define crypto_hashblocks crypto_hashblocks_sha512 +#define crypto_hashblocks_STATEBYTES crypto_hashblocks_sha512_STATEBYTES +#define crypto_hashblocks_BLOCKBYTES crypto_hashblocks_sha512_BLOCKBYTES +#define crypto_hashblocks_IMPLEMENTATION crypto_hashblocks_sha512_IMPLEMENTATION +#define crypto_hashblocks_VERSION crypto_hashblocks_sha512_VERSION +#define crypto_hashblocks_sha512_tweet_STATEBYTES 64 +#define crypto_hashblocks_sha512_tweet_BLOCKBYTES 128 +extern int crypto_hashblocks_sha512_tweet(unsigned char *,const unsigned char *,unsigned long long); +#define crypto_hashblocks_sha512_tweet_VERSION "-" +#define crypto_hashblocks_sha512 crypto_hashblocks_sha512_tweet +#define crypto_hashblocks_sha512_STATEBYTES crypto_hashblocks_sha512_tweet_STATEBYTES +#define crypto_hashblocks_sha512_BLOCKBYTES crypto_hashblocks_sha512_tweet_BLOCKBYTES +#define crypto_hashblocks_sha512_VERSION crypto_hashblocks_sha512_tweet_VERSION +#define crypto_hashblocks_sha512_IMPLEMENTATION "crypto_hashblocks/sha512/tweet" +#define crypto_hashblocks_sha256_tweet_STATEBYTES 32 +#define crypto_hashblocks_sha256_tweet_BLOCKBYTES 64 +extern int crypto_hashblocks_sha256_tweet(unsigned char *,const unsigned char *,unsigned long long); +#define crypto_hashblocks_sha256_tweet_VERSION "-" +#define crypto_hashblocks_sha256 crypto_hashblocks_sha256_tweet +#define crypto_hashblocks_sha256_STATEBYTES crypto_hashblocks_sha256_tweet_STATEBYTES +#define crypto_hashblocks_sha256_BLOCKBYTES crypto_hashblocks_sha256_tweet_BLOCKBYTES +#define crypto_hashblocks_sha256_VERSION crypto_hashblocks_sha256_tweet_VERSION +#define crypto_hashblocks_sha256_IMPLEMENTATION "crypto_hashblocks/sha256/tweet" +#define crypto_hash_PRIMITIVE "sha512" +#define crypto_hash crypto_hash_sha512 +#define crypto_hash_BYTES crypto_hash_sha512_BYTES +#define crypto_hash_IMPLEMENTATION crypto_hash_sha512_IMPLEMENTATION +#define crypto_hash_VERSION crypto_hash_sha512_VERSION +#define crypto_hash_sha512_tweet_BYTES 64 +extern int crypto_hash_sha512_tweet(unsigned char *,const unsigned char *,unsigned long long); +#define crypto_hash_sha512_tweet_VERSION "-" +#define crypto_hash_sha512 crypto_hash_sha512_tweet +#define crypto_hash_sha512_BYTES crypto_hash_sha512_tweet_BYTES +#define crypto_hash_sha512_VERSION crypto_hash_sha512_tweet_VERSION +#define crypto_hash_sha512_IMPLEMENTATION "crypto_hash/sha512/tweet" +#define crypto_hash_sha256_tweet_BYTES 32 +extern int crypto_hash_sha256_tweet(unsigned char *,const unsigned char *,unsigned long long); +#define crypto_hash_sha256_tweet_VERSION "-" +#define crypto_hash_sha256 crypto_hash_sha256_tweet +#define crypto_hash_sha256_BYTES crypto_hash_sha256_tweet_BYTES +#define crypto_hash_sha256_VERSION crypto_hash_sha256_tweet_VERSION +#define crypto_hash_sha256_IMPLEMENTATION "crypto_hash/sha256/tweet" +#define crypto_onetimeauth_PRIMITIVE "poly1305" +#define crypto_onetimeauth crypto_onetimeauth_poly1305 +#define crypto_onetimeauth_verify crypto_onetimeauth_poly1305_verify +#define crypto_onetimeauth_BYTES crypto_onetimeauth_poly1305_BYTES +#define crypto_onetimeauth_KEYBYTES crypto_onetimeauth_poly1305_KEYBYTES +#define crypto_onetimeauth_IMPLEMENTATION crypto_onetimeauth_poly1305_IMPLEMENTATION +#define crypto_onetimeauth_VERSION crypto_onetimeauth_poly1305_VERSION +#define crypto_onetimeauth_poly1305_tweet_BYTES 16 +#define crypto_onetimeauth_poly1305_tweet_KEYBYTES 32 +extern int crypto_onetimeauth_poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_onetimeauth_poly1305_tweet_verify(const unsigned char *,const unsigned char *,unsigned long long,const unsigned char *); +#define crypto_onetimeauth_poly1305_tweet_VERSION "-" +#define crypto_onetimeauth_poly1305 crypto_onetimeauth_poly1305_tweet +#define crypto_onetimeauth_poly1305_verify crypto_onetimeauth_poly1305_tweet_verify +#define crypto_onetimeauth_poly1305_BYTES crypto_onetimeauth_poly1305_tweet_BYTES +#define crypto_onetimeauth_poly1305_KEYBYTES crypto_onetimeauth_poly1305_tweet_KEYBYTES +#define crypto_onetimeauth_poly1305_VERSION crypto_onetimeauth_poly1305_tweet_VERSION +#define crypto_onetimeauth_poly1305_IMPLEMENTATION "crypto_onetimeauth/poly1305/tweet" +#define crypto_scalarmult_PRIMITIVE "curve25519" +#define crypto_scalarmult crypto_scalarmult_curve25519 +#define crypto_scalarmult_base crypto_scalarmult_curve25519_base +#define crypto_scalarmult_BYTES crypto_scalarmult_curve25519_BYTES +#define crypto_scalarmult_SCALARBYTES crypto_scalarmult_curve25519_SCALARBYTES +#define crypto_scalarmult_IMPLEMENTATION crypto_scalarmult_curve25519_IMPLEMENTATION +#define crypto_scalarmult_VERSION crypto_scalarmult_curve25519_VERSION +#define crypto_scalarmult_curve25519_tweet_BYTES 32 +#define crypto_scalarmult_curve25519_tweet_SCALARBYTES 32 +extern int crypto_scalarmult_curve25519_tweet(unsigned char *,const unsigned char *,const unsigned char *); +extern int crypto_scalarmult_curve25519_tweet_base(unsigned char *,const unsigned char *); +#define crypto_scalarmult_curve25519_tweet_VERSION "-" +#define crypto_scalarmult_curve25519 crypto_scalarmult_curve25519_tweet +#define crypto_scalarmult_curve25519_base crypto_scalarmult_curve25519_tweet_base +#define crypto_scalarmult_curve25519_BYTES crypto_scalarmult_curve25519_tweet_BYTES +#define crypto_scalarmult_curve25519_SCALARBYTES crypto_scalarmult_curve25519_tweet_SCALARBYTES +#define crypto_scalarmult_curve25519_VERSION crypto_scalarmult_curve25519_tweet_VERSION +#define crypto_scalarmult_curve25519_IMPLEMENTATION "crypto_scalarmult/curve25519/tweet" +#define crypto_secretbox_PRIMITIVE "xsalsa20poly1305" +#define crypto_secretbox crypto_secretbox_xsalsa20poly1305 +#define crypto_secretbox_open crypto_secretbox_xsalsa20poly1305_open +#define crypto_secretbox_KEYBYTES crypto_secretbox_xsalsa20poly1305_KEYBYTES +#define crypto_secretbox_NONCEBYTES crypto_secretbox_xsalsa20poly1305_NONCEBYTES +#define crypto_secretbox_ZEROBYTES crypto_secretbox_xsalsa20poly1305_ZEROBYTES +#define crypto_secretbox_BOXZEROBYTES crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES +#define crypto_secretbox_IMPLEMENTATION crypto_secretbox_xsalsa20poly1305_IMPLEMENTATION +#define crypto_secretbox_VERSION crypto_secretbox_xsalsa20poly1305_VERSION +#define crypto_secretbox_xsalsa20poly1305_tweet_KEYBYTES 32 +#define crypto_secretbox_xsalsa20poly1305_tweet_NONCEBYTES 24 +#define crypto_secretbox_xsalsa20poly1305_tweet_ZEROBYTES 32 +#define crypto_secretbox_xsalsa20poly1305_tweet_BOXZEROBYTES 16 +extern int crypto_secretbox_xsalsa20poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +extern int crypto_secretbox_xsalsa20poly1305_tweet_open(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +#define crypto_secretbox_xsalsa20poly1305_tweet_VERSION "-" +#define crypto_secretbox_xsalsa20poly1305 crypto_secretbox_xsalsa20poly1305_tweet +#define crypto_secretbox_xsalsa20poly1305_open crypto_secretbox_xsalsa20poly1305_tweet_open +#define crypto_secretbox_xsalsa20poly1305_KEYBYTES crypto_secretbox_xsalsa20poly1305_tweet_KEYBYTES +#define crypto_secretbox_xsalsa20poly1305_NONCEBYTES crypto_secretbox_xsalsa20poly1305_tweet_NONCEBYTES +#define crypto_secretbox_xsalsa20poly1305_ZEROBYTES crypto_secretbox_xsalsa20poly1305_tweet_ZEROBYTES +#define crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES crypto_secretbox_xsalsa20poly1305_tweet_BOXZEROBYTES +#define crypto_secretbox_xsalsa20poly1305_VERSION crypto_secretbox_xsalsa20poly1305_tweet_VERSION +#define crypto_secretbox_xsalsa20poly1305_IMPLEMENTATION "crypto_secretbox/xsalsa20poly1305/tweet" +#define crypto_sign_PRIMITIVE "ed25519" +#define crypto_sign crypto_sign_ed25519 +#define crypto_sign_open crypto_sign_ed25519_open +#define crypto_sign_keypair crypto_sign_ed25519_keypair +#define crypto_sign_BYTES crypto_sign_ed25519_BYTES +#define crypto_sign_PUBLICKEYBYTES crypto_sign_ed25519_PUBLICKEYBYTES +#define crypto_sign_SECRETKEYBYTES crypto_sign_ed25519_SECRETKEYBYTES +#define crypto_sign_IMPLEMENTATION crypto_sign_ed25519_IMPLEMENTATION +#define crypto_sign_VERSION crypto_sign_ed25519_VERSION +#define crypto_sign_ed25519_tweet_BYTES 64 +#define crypto_sign_ed25519_tweet_PUBLICKEYBYTES 32 +#define crypto_sign_ed25519_tweet_SECRETKEYBYTES 64 +extern int crypto_sign_ed25519_tweet(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_sign_ed25519_tweet_open(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *); +extern int crypto_sign_ed25519_tweet_keypair(unsigned char *,unsigned char *); +#define crypto_sign_ed25519_tweet_VERSION "-" +#define crypto_sign_ed25519 crypto_sign_ed25519_tweet +#define crypto_sign_ed25519_open crypto_sign_ed25519_tweet_open +#define crypto_sign_ed25519_keypair crypto_sign_ed25519_tweet_keypair +#define crypto_sign_ed25519_BYTES crypto_sign_ed25519_tweet_BYTES +#define crypto_sign_ed25519_PUBLICKEYBYTES crypto_sign_ed25519_tweet_PUBLICKEYBYTES +#define crypto_sign_ed25519_SECRETKEYBYTES crypto_sign_ed25519_tweet_SECRETKEYBYTES +#define crypto_sign_ed25519_VERSION crypto_sign_ed25519_tweet_VERSION +#define crypto_sign_ed25519_IMPLEMENTATION "crypto_sign/ed25519/tweet" +#define crypto_stream_PRIMITIVE "xsalsa20" +#define crypto_stream crypto_stream_xsalsa20 +#define crypto_stream_xor crypto_stream_xsalsa20_xor +#define crypto_stream_KEYBYTES crypto_stream_xsalsa20_KEYBYTES +#define crypto_stream_NONCEBYTES crypto_stream_xsalsa20_NONCEBYTES +#define crypto_stream_IMPLEMENTATION crypto_stream_xsalsa20_IMPLEMENTATION +#define crypto_stream_VERSION crypto_stream_xsalsa20_VERSION +#define crypto_stream_xsalsa20_tweet_KEYBYTES 32 +#define crypto_stream_xsalsa20_tweet_NONCEBYTES 24 +extern int crypto_stream_xsalsa20_tweet(unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +extern int crypto_stream_xsalsa20_tweet_xor(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +#define crypto_stream_xsalsa20_tweet_VERSION "-" +#define crypto_stream_xsalsa20 crypto_stream_xsalsa20_tweet +#define crypto_stream_xsalsa20_xor crypto_stream_xsalsa20_tweet_xor +#define crypto_stream_xsalsa20_KEYBYTES crypto_stream_xsalsa20_tweet_KEYBYTES +#define crypto_stream_xsalsa20_NONCEBYTES crypto_stream_xsalsa20_tweet_NONCEBYTES +#define crypto_stream_xsalsa20_VERSION crypto_stream_xsalsa20_tweet_VERSION +#define crypto_stream_xsalsa20_IMPLEMENTATION "crypto_stream/xsalsa20/tweet" +#define crypto_stream_salsa20_tweet_KEYBYTES 32 +#define crypto_stream_salsa20_tweet_NONCEBYTES 8 +extern int crypto_stream_salsa20_tweet(unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +extern int crypto_stream_salsa20_tweet_xor(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *); +#define crypto_stream_salsa20_tweet_VERSION "-" +#define crypto_stream_salsa20 crypto_stream_salsa20_tweet +#define crypto_stream_salsa20_xor crypto_stream_salsa20_tweet_xor +#define crypto_stream_salsa20_KEYBYTES crypto_stream_salsa20_tweet_KEYBYTES +#define crypto_stream_salsa20_NONCEBYTES crypto_stream_salsa20_tweet_NONCEBYTES +#define crypto_stream_salsa20_VERSION crypto_stream_salsa20_tweet_VERSION +#define crypto_stream_salsa20_IMPLEMENTATION "crypto_stream/salsa20/tweet" +#define crypto_verify_PRIMITIVE "16" +#define crypto_verify crypto_verify_16 +#define crypto_verify_BYTES crypto_verify_16_BYTES +#define crypto_verify_IMPLEMENTATION crypto_verify_16_IMPLEMENTATION +#define crypto_verify_VERSION crypto_verify_16_VERSION +#define crypto_verify_16_tweet_BYTES 16 +extern int crypto_verify_16_tweet(const unsigned char *,const unsigned char *); +#define crypto_verify_16_tweet_VERSION "-" +#define crypto_verify_16 crypto_verify_16_tweet +#define crypto_verify_16_BYTES crypto_verify_16_tweet_BYTES +#define crypto_verify_16_VERSION crypto_verify_16_tweet_VERSION +#define crypto_verify_16_IMPLEMENTATION "crypto_verify/16/tweet" +#define crypto_verify_32_tweet_BYTES 32 +extern int crypto_verify_32_tweet(const unsigned char *,const unsigned char *); +#define crypto_verify_32_tweet_VERSION "-" +#define crypto_verify_32 crypto_verify_32_tweet +#define crypto_verify_32_BYTES crypto_verify_32_tweet_BYTES +#define crypto_verify_32_VERSION crypto_verify_32_tweet_VERSION +#define crypto_verify_32_IMPLEMENTATION "crypto_verify/32/tweet" +#endif