summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastien Blot2017-09-20 10:11:01 +0200
committerSebastien Blot2017-09-20 10:11:01 +0200
commit868f96c759b6650d88ff9f4fbc5c048302134248 (patch)
treec0de0af318bf77a8959164ef11aeeeb2b7bab294
Initial import
-rw-r--r--.gitignore39
-rw-r--r--LICENSE165
-rw-r--r--Makefile32
-rw-r--r--README.md15
-rw-r--r--config/default.ini54
-rw-r--r--config/examples.ini47
-rw-r--r--debian/changelog5
-rw-r--r--debian/compat1
-rw-r--r--debian/control14
-rw-r--r--debian/copyright8
-rw-r--r--debian/docs1
-rwxr-xr-xdebian/rules27
-rw-r--r--debian/watch2
-rw-r--r--doc/Makefile20
-rw-r--r--doc/source/_static/custom.css4
-rw-r--r--doc/source/_static/sp.jpgbin0 -> 36559 bytes
-rw-r--r--doc/source/conf.py178
-rw-r--r--doc/source/config.rst283
-rw-r--r--doc/source/download.rst16
-rw-r--r--doc/source/faq.rst196
-rw-r--r--doc/source/features.rst352
-rw-r--r--doc/source/index.rst30
-rw-r--r--doc/source/installation.rst33
-rw-r--r--doc/source/papers.rst16
-rw-r--r--scripts/generate_rules.php43
-rw-r--r--src/bench/bench.php422
-rw-r--r--src/bench/micro_bench.php358
-rw-r--r--src/config.m435
-rw-r--r--src/config.w3213
-rw-r--r--src/php_snuffleupagus.h71
-rw-r--r--src/snuffleupagus.c222
-rw-r--r--src/snuffleupagus.php21
-rw-r--r--src/sp_compile.c53
-rw-r--r--src/sp_compile.h6
-rw-r--r--src/sp_config.c193
-rw-r--r--src/sp_config.h206
-rw-r--r--src/sp_config_keywords.c268
-rw-r--r--src/sp_config_keywords.h16
-rw-r--r--src/sp_config_utils.c211
-rw-r--r--src/sp_config_utils.h8
-rw-r--r--src/sp_cookie_encryption.c216
-rw-r--r--src/sp_cookie_encryption.h17
-rw-r--r--src/sp_disable_xxe.c25
-rw-r--r--src/sp_disable_xxe.h6
-rw-r--r--src/sp_disabled_functions.c356
-rw-r--r--src/sp_disabled_functions.h11
-rw-r--r--src/sp_execute.c100
-rw-r--r--src/sp_execute.h6
-rw-r--r--src/sp_harden_rand.c77
-rw-r--r--src/sp_harden_rand.h10
-rw-r--r--src/sp_list.c37
-rw-r--r--src/sp_list.h15
-rw-r--r--src/sp_network_utils.c159
-rw-r--r--src/sp_network_utils.h8
-rw-r--r--src/sp_unserialize.c111
-rw-r--r--src/sp_unserialize.h9
-rw-r--r--src/sp_upload_validation.c92
-rw-r--r--src/sp_upload_validation.h9
-rw-r--r--src/sp_utils.c425
-rw-r--r--src/sp_utils.h68
-rw-r--r--src/tests/broken_conf.phpt10
-rw-r--r--src/tests/broken_conf2.phpt9
-rw-r--r--src/tests/broken_conf_config_regexp.phpt10
-rw-r--r--src/tests/broken_conf_enable_disable.phpt9
-rw-r--r--src/tests/broken_conf_expecting_bool.phpt9
-rw-r--r--src/tests/broken_conf_expecting_int.phpt9
-rw-r--r--src/tests/broken_conf_invalid_cidr.phpt9
-rw-r--r--src/tests/broken_conf_invalid_cidr6.phpt9
-rw-r--r--src/tests/broken_conf_invalid_cidr6_no_slash.phpt9
-rw-r--r--src/tests/broken_conf_invalid_cidr6_too_big.phpt9
-rw-r--r--src/tests/broken_conf_invalid_cidr_value.phpt11
-rw-r--r--src/tests/broken_conf_invalid_type.phpt9
-rw-r--r--src/tests/broken_conf_line_empty_string.phpt9
-rw-r--r--src/tests/broken_conf_line_no_closing.phpt9
-rw-r--r--src/tests/broken_conf_line_too_long.phpt10
-rw-r--r--src/tests/broken_conf_lots_of_quotes.phpt9
-rw-r--r--src/tests/broken_conf_mutually_exclusive.phpt9
-rw-r--r--src/tests/broken_conf_mutually_exclusive2.phpt9
-rw-r--r--src/tests/broken_conf_mutually_exclusive3.phpt9
-rw-r--r--src/tests/broken_conf_mutually_exclusive4.phpt9
-rw-r--r--src/tests/broken_conf_mutually_exclusive5.phpt9
-rw-r--r--src/tests/broken_conf_mutually_exclusive6.phpt9
-rw-r--r--src/tests/broken_conf_mutually_exclusive7.phpt9
-rw-r--r--src/tests/broken_conf_mutually_exclusive8.phpt9
-rw-r--r--src/tests/broken_conf_no_closing_misc.phpt10
-rw-r--r--src/tests/broken_conf_weird_keyword.phpt9
-rw-r--r--src/tests/broken_conf_wrong_quotes.phpt9
-rw-r--r--src/tests/broken_conf_wrong_type.phpt9
-rw-r--r--src/tests/broken_regexp.phpt9
-rw-r--r--src/tests/config/borken_conf_enable_disable.ini1
-rw-r--r--src/tests/config/broken_conf.ini1
-rw-r--r--src/tests/config/broken_conf2.ini1
-rw-r--r--src/tests/config/broken_conf_expecting_bool.ini5
-rw-r--r--src/tests/config/broken_conf_expecting_int.ini2
-rw-r--r--src/tests/config/broken_conf_invalid_cidr.ini1
-rw-r--r--src/tests/config/broken_conf_invalid_cidr6.ini1
-rw-r--r--src/tests/config/broken_conf_invalid_cidr6_no_slash.ini1
-rw-r--r--src/tests/config/broken_conf_invalid_cidr6_too_big.ini1
-rw-r--r--src/tests/config/broken_conf_invalid_cidr_value.ini1
-rw-r--r--src/tests/config/broken_conf_invalid_type.ini1
-rw-r--r--src/tests/config/broken_conf_line_empty_string.ini1
-rw-r--r--src/tests/config/broken_conf_line_no_closing.ini1
-rw-r--r--src/tests/config/broken_conf_line_too_long.ini1
-rw-r--r--src/tests/config/broken_conf_lots_of_quotes.ini1
-rw-r--r--src/tests/config/broken_conf_mutually_exclusive.ini1
-rw-r--r--src/tests/config/broken_conf_mutually_exclusive2.ini1
-rw-r--r--src/tests/config/broken_conf_mutually_exclusive3.ini1
-rw-r--r--src/tests/config/broken_conf_mutually_exclusive4.ini1
-rw-r--r--src/tests/config/broken_conf_mutually_exclusive5.ini1
-rw-r--r--src/tests/config/broken_conf_mutually_exclusive6.ini1
-rw-r--r--src/tests/config/broken_conf_mutually_exclusive7.ini1
-rw-r--r--src/tests/config/broken_conf_mutually_exclusive8.ini1
-rw-r--r--src/tests/config/broken_conf_no_closing_misc.ini1
-rw-r--r--src/tests/config/broken_conf_to_few_args.ini1
-rw-r--r--src/tests/config/broken_conf_weird_keyword.ini1
-rw-r--r--src/tests/config/broken_conf_wrong_quotes.ini1
-rw-r--r--src/tests/config/broken_conf_wrong_type.ini5
-rw-r--r--src/tests/config/broken_config_regexp.ini1
-rw-r--r--src/tests/config/broken_regexp.ini1
-rw-r--r--src/tests/config/config_disable_writable.ini1
-rw-r--r--src/tests/config/config_disable_writable_disabled.ini1
-rw-r--r--src/tests/config/config_disable_writable_simulation.ini1
-rw-r--r--src/tests/config/config_disabled_functions_filename_r.ini2
-rw-r--r--src/tests/config/config_disabled_functions_method.ini3
-rw-r--r--src/tests/config/config_disabled_functions_name_r.ini2
-rw-r--r--src/tests/config/config_disabled_functions_name_type.ini1
-rw-r--r--src/tests/config/config_disabled_functions_namespace.ini2
-rw-r--r--src/tests/config/config_disabled_functions_nul_byte.ini1
-rw-r--r--src/tests/config/config_disabled_functions_param.ini6
-rw-r--r--src/tests/config/config_disabled_functions_param_alias.ini2
-rw-r--r--src/tests/config/config_disabled_functions_param_allow.ini3
-rw-r--r--src/tests/config/config_disabled_functions_param_array.ini4
-rw-r--r--src/tests/config/config_disabled_functions_param_int.ini2
-rw-r--r--src/tests/config/config_disabled_functions_param_r.ini1
-rw-r--r--src/tests/config/config_disabled_functions_param_runtime.ini1
-rw-r--r--src/tests/config/config_disabled_functions_param_str_representation.ini1
-rw-r--r--src/tests/config/config_disabled_functions_require.ini1
-rw-r--r--src/tests/config/config_disabled_functions_ret_allow.ini2
-rw-r--r--src/tests/config/config_disabled_functions_ret_allow_value.ini1
-rw-r--r--src/tests/config/config_disabled_functions_ret_right_hash.ini4
-rw-r--r--src/tests/config/config_disabled_functions_ret_simulation.ini3
-rw-r--r--src/tests/config/config_disabled_functions_right_hash.ini3
-rw-r--r--src/tests/config/config_disabled_user_functions.ini1
-rw-r--r--src/tests/config/config_encrypted_cookies.ini3
-rw-r--r--src/tests/config/config_noncore_function_hooking.ini1
-rw-r--r--src/tests/config/config_rand_harden_disabled.ini1
-rw-r--r--src/tests/config/config_serialize.ini2
-rw-r--r--src/tests/config/config_serialize_sim.ini2
-rw-r--r--src/tests/config/disable_xxe.ini1
-rw-r--r--src/tests/config/disable_xxe_disable.ini1
-rw-r--r--src/tests/config/disabled_function_local_var.ini2
-rw-r--r--src/tests/config/disabled_function_super_global_var.ini1
-rw-r--r--src/tests/config/disabled_functions.ini7
-rw-r--r--src/tests/config/disabled_functions_cidr.ini4
-rw-r--r--src/tests/config/disabled_functions_mb.ini2
-rw-r--r--src/tests/config/disabled_functions_ret.ini5
-rw-r--r--src/tests/config/disabled_functions_ret_type.ini1
-rw-r--r--src/tests/config/disabled_functions_ret_type_double.ini1
-rw-r--r--src/tests/config/disabled_functions_ret_type_long.ini1
-rw-r--r--src/tests/config/disabled_functions_ret_type_resource.ini1
-rw-r--r--src/tests/config/disabled_functions_ret_type_str.ini1
-rw-r--r--src/tests/config/disabled_functions_ret_type_true.ini1
-rw-r--r--src/tests/config/disabled_functions_retval.ini1
-rw-r--r--src/tests/config/disabled_functions_retval_rx.ini1
-rw-r--r--src/tests/config/disabled_functions_zero_cidr.ini1
-rw-r--r--src/tests/config/dump_request.ini1
-rw-r--r--src/tests/config/dump_request_invalid_folder.ini1
-rw-r--r--src/tests/config/empty.ini0
-rw-r--r--src/tests/config/empty_conf.ini0
-rw-r--r--src/tests/config/encryption_key_only.ini1
-rw-r--r--src/tests/config/global_strict.ini1
-rw-r--r--src/tests/config/global_strict_disabled.ini1
-rw-r--r--src/tests/config/harden_rand.ini1
-rw-r--r--src/tests/config/upload_validation.ini2
-rw-r--r--src/tests/config/upload_validation_invalid.ini1
-rw-r--r--src/tests/config/upload_validation_ko.ini1
-rw-r--r--src/tests/config/upload_validation_ko_simulation.ini1
-rw-r--r--src/tests/config/upload_validation_no_exist.ini1
-rw-r--r--src/tests/config/upload_validation_non_exec.ini1
-rw-r--r--src/tests/config/upload_validation_ok.ini1
-rwxr-xr-xsrc/tests/data/upload_invalid.sh1
-rwxr-xr-xsrc/tests/data/upload_ko.sh2
-rw-r--r--src/tests/data/upload_no_exec.sh2
-rwxr-xr-xsrc/tests/data/upload_ok.sh2
-rw-r--r--src/tests/deny_writable_execution.phpt44
-rw-r--r--src/tests/deny_writable_execution_disabled.phpt32
-rw-r--r--src/tests/deny_writable_execution_simulation.phpt45
-rw-r--r--src/tests/disable_xxe_dom.phpt71
-rw-r--r--src/tests/disable_xxe_dom_disabled.phpt56
-rw-r--r--src/tests/disable_xxe_simplexml.phpt52
-rw-r--r--src/tests/disable_xxe_simplexml_oop.phpt52
-rw-r--r--src/tests/disable_xxe_xml_parse.phpt104
-rw-r--r--src/tests/disabled_function_local_var.phpt24
-rw-r--r--src/tests/disabled_function_super_global_var.phpt20
-rw-r--r--src/tests/disabled_functions.phpt21
-rw-r--r--src/tests/disabled_functions_cidr.phpt18
-rw-r--r--src/tests/disabled_functions_cidr_6.phpt18
-rw-r--r--src/tests/disabled_functions_filename_r.phpt14
-rw-r--r--src/tests/disabled_functions_mb.phpt12
-rw-r--r--src/tests/disabled_functions_method.phpt29
-rw-r--r--src/tests/disabled_functions_name_r.phpt15
-rw-r--r--src/tests/disabled_functions_name_type.phpt14
-rw-r--r--src/tests/disabled_functions_namespace.phpt31
-rw-r--r--src/tests/disabled_functions_noconf.phpt12
-rw-r--r--src/tests/disabled_functions_nul_byte.phpt15
-rw-r--r--src/tests/disabled_functions_param.phpt24
-rw-r--r--src/tests/disabled_functions_param_alias.phpt14
-rw-r--r--src/tests/disabled_functions_param_allow.phpt14
-rw-r--r--src/tests/disabled_functions_param_array.phpt37
-rw-r--r--src/tests/disabled_functions_param_int.phpt25
-rw-r--r--src/tests/disabled_functions_param_r.phpt14
-rw-r--r--src/tests/disabled_functions_param_str_representation.phpt25
-rw-r--r--src/tests/disabled_functions_parse_class.phpt22
-rw-r--r--src/tests/disabled_functions_require.phpt25
-rw-r--r--src/tests/disabled_functions_ret.phpt13
-rw-r--r--src/tests/disabled_functions_ret2.phpt12
-rw-r--r--src/tests/disabled_functions_ret3.phpt22
-rw-r--r--src/tests/disabled_functions_ret_allow.phpt13
-rw-r--r--src/tests/disabled_functions_ret_allow_value.phpt12
-rw-r--r--src/tests/disabled_functions_ret_right_hash.phpt12
-rw-r--r--src/tests/disabled_functions_ret_simulation.phpt18
-rw-r--r--src/tests/disabled_functions_ret_type.phpt16
-rw-r--r--src/tests/disabled_functions_ret_type_double.phpt12
-rw-r--r--src/tests/disabled_functions_ret_type_long.phpt12
-rw-r--r--src/tests/disabled_functions_ret_type_resource.phpt12
-rw-r--r--src/tests/disabled_functions_ret_type_str.phpt12
-rw-r--r--src/tests/disabled_functions_ret_type_true.phpt16
-rw-r--r--src/tests/disabled_functions_ret_val.phpt14
-rw-r--r--src/tests/disabled_functions_ret_val_rx.phpt14
-rw-r--r--src/tests/disabled_functions_right_hash.phpt12
-rw-r--r--src/tests/disabled_functions_runtime.phpt31
-rw-r--r--src/tests/disabled_functions_zero_cidr.phpt18
-rw-r--r--src/tests/disabled_option.phpt16
-rw-r--r--src/tests/disabled_user_functions.phpt15
-rw-r--r--src/tests/dump_request.phpt39
-rw-r--r--src/tests/dump_request_invalid_folder.phpt25
-rw-r--r--src/tests/dump_request_too_big.phpt42
-rw-r--r--src/tests/empty_conf.phpt8
-rw-r--r--src/tests/encrypt_cookies.phpt22
-rw-r--r--src/tests/encrypt_cookies2.phpt23
-rw-r--r--src/tests/encrypt_cookies3.phpt23
-rw-r--r--src/tests/encrypt_cookies4.phpt23
-rw-r--r--src/tests/encrypt_cookies_invalid_decryption.phpt23
-rw-r--r--src/tests/encrypt_cookies_invalid_decryption2.phpt23
-rw-r--r--src/tests/encrypt_cookies_invalid_decryption3.phpt21
-rw-r--r--src/tests/encryption_key_only.phpt13
-rw-r--r--src/tests/example_configuration.phpt12
-rw-r--r--src/tests/global_strict.phpt16
-rw-r--r--src/tests/global_strict_disabled.phpt14
-rw-r--r--src/tests/harden_mt_rand.phpt22
-rw-r--r--src/tests/harden_rand.phpt24
-rw-r--r--src/tests/harden_rand_noargs.phpt62
-rw-r--r--src/tests/inexistent_conf_file.phpt10
-rw-r--r--src/tests/loading.phpt10
-rw-r--r--src/tests/noncore_function_hooking.phpt15
-rw-r--r--src/tests/phpinfo_presence.phpt19
-rw-r--r--src/tests/serialize.phpt13
-rw-r--r--src/tests/setcookie.phpt35
-rw-r--r--src/tests/shipped_configuration.phpt12
-rw-r--r--src/tests/unserialize.phpt13
-rw-r--r--src/tests/unserialize_fail.phpt23
-rw-r--r--src/tests/unserialize_sim.phpt17
-rw-r--r--src/tests/upload_validation.phpt16
-rw-r--r--src/tests/upload_validation_invalid.phpt17
-rw-r--r--src/tests/upload_validation_ko.phpt14
-rw-r--r--src/tests/upload_validation_no_exec.phpt32
-rw-r--r--src/tests/upload_validation_nocrash.phpt12
-rw-r--r--src/tests/upload_validation_ok.phpt17
-rw-r--r--src/tweetnacl.c842
-rw-r--r--src/tweetnacl.h277
270 files changed, 8888 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..68dfa7a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,39 @@
1*.txt
2*.orig
3*.gdbhistory
4*.swp
5.deps
6.libs
7*.lo
8src/tests/*.diff
9src/tests/*.exp
10src/tests/*.log
11src/tests/*.out
12src/tests/*.sh
13src/tests/*.php
14# Files generated by phpize, configure and make
15src/autom4te.cache
16src/build
17src/modules
18src/acinclude.m4
19src/aclocal.m4
20src/config.guess
21src/config.h
22src/config.h.in
23src/config.log
24src/config.nice
25src/config.status
26src/config.sub
27src/configure
28src/configure.in
29src/install-sh
30src/*.la
31src/ltmain.sh
32src/libtool
33src/Makefile
34src/Makefile.fragments
35src/Makefile.global
36src/Makefile.objects
37src/missing
38src/mkinstalldirs
39src/run-tests.php
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..65c5ca8
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
1 GNU LESSER GENERAL PUBLIC LICENSE
2 Version 3, 29 June 2007
3
4 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8
9 This version of the GNU Lesser General Public License incorporates
10the terms and conditions of version 3 of the GNU General Public
11License, supplemented by the additional permissions listed below.
12
13 0. Additional Definitions.
14
15 As used herein, "this License" refers to version 3 of the GNU Lesser
16General Public License, and the "GNU GPL" refers to version 3 of the GNU
17General Public License.
18
19 "The Library" refers to a covered work governed by this License,
20other than an Application or a Combined Work as defined below.
21
22 An "Application" is any work that makes use of an interface provided
23by the Library, but which is not otherwise based on the Library.
24Defining a subclass of a class defined by the Library is deemed a mode
25of using an interface provided by the Library.
26
27 A "Combined Work" is a work produced by combining or linking an
28Application with the Library. The particular version of the Library
29with which the Combined Work was made is also called the "Linked
30Version".
31
32 The "Minimal Corresponding Source" for a Combined Work means the
33Corresponding Source for the Combined Work, excluding any source code
34for portions of the Combined Work that, considered in isolation, are
35based on the Application, and not on the Linked Version.
36
37 The "Corresponding Application Code" for a Combined Work means the
38object code and/or source code for the Application, including any data
39and utility programs needed for reproducing the Combined Work from the
40Application, but excluding the System Libraries of the Combined Work.
41
42 1. Exception to Section 3 of the GNU GPL.
43
44 You may convey a covered work under sections 3 and 4 of this License
45without being bound by section 3 of the GNU GPL.
46
47 2. Conveying Modified Versions.
48
49 If you modify a copy of the Library, and, in your modifications, a
50facility refers to a function or data to be supplied by an Application
51that uses the facility (other than as an argument passed when the
52facility is invoked), then you may convey a copy of the modified
53version:
54
55 a) under this License, provided that you make a good faith effort to
56 ensure that, in the event an Application does not supply the
57 function or data, the facility still operates, and performs
58 whatever part of its purpose remains meaningful, or
59
60 b) under the GNU GPL, with none of the additional permissions of
61 this License applicable to that copy.
62
63 3. Object Code Incorporating Material from Library Header Files.
64
65 The object code form of an Application may incorporate material from
66a header file that is part of the Library. You may convey such object
67code under terms of your choice, provided that, if the incorporated
68material is not limited to numerical parameters, data structure
69layouts and accessors, or small macros, inline functions and templates
70(ten or fewer lines in length), you do both of the following:
71
72 a) Give prominent notice with each copy of the object code that the
73 Library is used in it and that the Library and its use are
74 covered by this License.
75
76 b) Accompany the object code with a copy of the GNU GPL and this license
77 document.
78
79 4. Combined Works.
80
81 You may convey a Combined Work under terms of your choice that,
82taken together, effectively do not restrict modification of the
83portions of the Library contained in the Combined Work and reverse
84engineering for debugging such modifications, if you also do each of
85the following:
86
87 a) Give prominent notice with each copy of the Combined Work that
88 the Library is used in it and that the Library and its use are
89 covered by this License.
90
91 b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 document.
93
94 c) For a Combined Work that displays copyright notices during
95 execution, include the copyright notice for the Library among
96 these notices, as well as a reference directing the user to the
97 copies of the GNU GPL and this license document.
98
99 d) Do one of the following:
100
101 0) Convey the Minimal Corresponding Source under the terms of this
102 License, and the Corresponding Application Code in a form
103 suitable for, and under terms that permit, the user to
104 recombine or relink the Application with a modified version of
105 the Linked Version to produce a modified Combined Work, in the
106 manner specified by section 6 of the GNU GPL for conveying
107 Corresponding Source.
108
109 1) Use a suitable shared library mechanism for linking with the
110 Library. A suitable mechanism is one that (a) uses at run time
111 a copy of the Library already present on the user's computer
112 system, and (b) will operate properly with a modified version
113 of the Library that is interface-compatible with the Linked
114 Version.
115
116 e) Provide Installation Information, but only if you would otherwise
117 be required to provide such information under section 6 of the
118 GNU GPL, and only to the extent that such information is
119 necessary to install and execute a modified version of the
120 Combined Work produced by recombining or relinking the
121 Application with a modified version of the Linked Version. (If
122 you use option 4d0, the Installation Information must accompany
123 the Minimal Corresponding Source and Corresponding Application
124 Code. If you use option 4d1, you must provide the Installation
125 Information in the manner specified by section 6 of the GNU GPL
126 for conveying Corresponding Source.)
127
128 5. Combined Libraries.
129
130 You may place library facilities that are a work based on the
131Library side by side in a single library together with other library
132facilities that are not Applications and are not covered by this
133License, and convey such a combined library under terms of your
134choice, if you do both of the following:
135
136 a) Accompany the combined library with a copy of the same work based
137 on the Library, uncombined with any other library facilities,
138 conveyed under the terms of this License.
139
140 b) Give prominent notice with the combined library that part of it
141 is a work based on the Library, and explaining where to find the
142 accompanying uncombined form of the same work.
143
144 6. Revised Versions of the GNU Lesser General Public License.
145
146 The Free Software Foundation may publish revised and/or new versions
147of the GNU Lesser General Public License from time to time. Such new
148versions will be similar in spirit to the present version, but may
149differ in detail to address new problems or concerns.
150
151 Each version is given a distinguishing version number. If the
152Library as you received it specifies that a certain numbered version
153of the GNU Lesser General Public License "or any later version"
154applies to it, you have the option of following the terms and
155conditions either of that published version or of any later version
156published by the Free Software Foundation. If the Library as you
157received it does not specify a version number of the GNU Lesser
158General Public License, you may choose any version of the GNU Lesser
159General Public License ever published by the Free Software Foundation.
160
161 If the Library as you received it specifies that a proxy can decide
162whether future versions of the GNU Lesser General Public License shall
163apply, that proxy's public statement of acceptance of any version is
164permanent authorization for you to choose that version for the
165Library.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..869f9fb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,32 @@
1clean:
2 make -C src clean
3 cd src; phpize --clean
4
5debug:
6 cd src; phpize
7 export CFLAGS="-Wall -Wextra -g3 -ggdb -Wno-unused-function"; cd src; ./configure --enable-snuffleupagus --enable-debug
8 make -C src
9 TEST_PHP_ARGS='-q' REPORT_EXIT_STATUS=1 make -C src test
10
11coverage:
12 cd src; phpize
13 export CFLAGS="--coverage -fprofile-arcs -ftest-coverage -O0"; export LDFLAGS="--coverage"; cd src; ./configure --enable-snuffleupagus --enable-coverage
14 make -C src
15 lcov --base-directory src --directory ./src --zerocounters -q --rc lcov_branch_coverage=1
16 rm -Rf src/COV.html
17 TEST_PHP_ARGS='-q' REPORT_EXIT_STATUS=1 make -C src test
18 lcov --base-directory ./src --directory src -c -o ./src/COV.info --rc lcov_branch_coverage=1 2>/dev/null 1>/dev/null
19 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
20 genhtml -o src/COV.html ./src/COV.info --branch-coverage
21
22tests: joomla
23
24joomla:
25 if [ -nd "joomla-cms" ]; then git clone --depth 1 git@github.com:joomla/joomla-cms.git; fi
26 cd joomla-cms; composer install
27 cd joomla-cms; libraries/vendor/phpunit/phpunit/phpunit -d extension=./src/modules/snuffleupagus.so
28
29packages: debian
30
31debian:
32 dpkg-buildpackage -i -us -uc -tc -I -rfakeroot
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..53b9003
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
1[![build
2status](https://gitlab.nbs-system.com/secu/snuffleupagus/badges/master/build.svg)](https://gitlab.nbs-system.com/secu/snuffleupagus/commits/master)
3[![coverage
4report](https://gitlab.nbs-system.com/secu/snuffleupagus/badges/master/coverage.svg)](https://gitlab.nbs-system.com/secu/snuffleupagus/commits/master)
5[Documentation]( https://secu.gitlab-pages.nbs-system.com/snuffleupagus/ )
6
7# Code style
8We're using [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to
9ensure a consistent code-style. Please run it with `clang-format -style=google`
10before committing, or even better, use a [pre-commit hook](https://github.com/andrewseidl/githook-clang-format)
11
12# Resources
13- 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
14- [Upgrading to php-ng]( https://wiki.php.net/phpng-upgrading )
15- 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 0000000..0f67632
--- /dev/null
+++ b/config/default.ini
@@ -0,0 +1,54 @@
1# Harden the `chmod` function
2sp.disable_functions.function("chmod").param("mode").value_r("^[0-9]{2}[67]$").drop();
3sp.disable_functions.function("chmod").param("mode").value_r("o\\+w$").drop();
4
5# Prevent various `mail`-related vulnerabilities
6sp.disable_functions.function("mail").param("additional_parameters").value_r("\\-").drop();
7
8##Prevent various `include`-related vulnerabilities
9sp.disable_functions.function_r("^(?:require|include)_once$").value_r("\\.(?:php|php7|inc|tpl)$").allow();
10sp.disable_functions.function_r("^require|include$").value_r("\\.(?:php|php7|inc|tpl)$").allow();
11sp.disable_functions.function_r("^(?:require|include)_once$").drop();
12sp.disable_functions.function_r("^require|include$").drop();
13
14# Prevent `system`-related injections
15sp.disable_functions.function("system").param("command").value_r("[$|;&`\\n]").drop();
16sp.disable_functions.function("shell_exec").param("command").value_r("[$|;&`\\n]").drop();
17sp.disable_functions.function("exec").param("command").value_r("[$|;&`\\n]").drop();
18sp.disable_functions.function("proc_open").param("command").value_r("[$|;&`\\n]").drop();
19
20# Prevent runtime modification of interesting things
21sp.disable_functions.function("ini_set").param("var_name").value("assert.active").drop();
22sp.disable_functions.function("ini_set").param("var_name").value("zend.assertions").drop();
23sp.disable_functions.function("ini_set").param("var_name").value("memory_limit").drop();
24sp.disable_functions.function("ini_set").param("var_name").value("include_path").drop();
25sp.disable_functions.function("ini_set").param("var_name").value("open_basedir").drop();
26
27# Detect some backdoors via environnement recon
28sp.disable_functions.function("ini_get").param("var_name").value_r("(?:allow_url_fopen|open_basedir|suhosin)").drop();
29sp.disable_functions.function("function_exists").param("function_name").value_r("(?:eval|exec|system)").drop();
30sp.disable_functions.function("is_callable").param("var").value_r("(?:eval|exec|system)").drop();
31
32# Ghetto sqli hardening
33sp.disable_functions.function_r("mysqli?_query").param("query").value_r("/\\*").drop();
34sp.disable_functions.function_r("mysqli?_query").param("query").value_r("--").drop();
35sp.disable_functions.function_r("mysqli?_query").param("query").value_r("#").drop();
36sp.disable_functions.function_r("mysqli?_query").param("query").value_r(";.*;").drop();
37sp.disable_functions.function_r("mysqli?_query").param("query").value_r("benchmark").drop();
38sp.disable_functions.function_r("mysqli?_query").param("query").value_r("sleep").drop();
39sp.disable_functions.function_r("mysqli?_query").param("query").value_r("information_schema").drop();
40sp.disable_functions.function("PDO::query").param("query").value_r("/\\*").drop();
41sp.disable_functions.function("PDO::query").param("query").value_r("--").drop();
42sp.disable_functions.function("PDO::query").param("query").value_r("#").drop();
43sp.disable_functions.function("PDO::query").param("query").value_r(";.*;").drop();
44sp.disable_functions.function("PDO::query").param("query").value_r("benchmark\\s*\\(").drop();
45sp.disable_functions.function("PDO::query").param("query").value_r("sleep\\s*\\(").drop();
46sp.disable_functions.function("PDO::query").param("query").value_r("information_schema").drop();
47
48# Ghetto sqli detection
49sp.disable_functions.function_r("mysqli?_query").ret("FALSE").drop();
50sp.disable_functions.function_r("PDO::query").ret("FALSE").drop();
51
52#File upload
53sp.disable_functions.function("move_uploaded_file").param("destination").value_r("\\.ph").drop();
54sp.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 0000000..d7599fb
--- /dev/null
+++ b/config/examples.ini
@@ -0,0 +1,47 @@
1# Restrict system calls to specific file
2sp.disable_functions.function("system").filename("update.php").allow();
3sp.disable_functions.function("system").drop();
4
5
6# Restrict system calls to specific file with a specific hash
7sp.disable_functions.function("system").filename("update.php").hash("d27c6c5686bc129716b6aac8dfefe2d519a80eb6cc144e97ad42c728d423eed0").allow();
8sp.disable_functions.function("system").drop();
9
10
11# AbanteCart 1.2.8 - Multiple SQL Injections <https://blog.ripstech.com/2016/abantecart-multiple-sql-injections>
12sp.disable_functions.filename("static_pages/index.php").var("_SERVER[PHP_SELF").value_r("\"").drop().alias("XSS");
13sp.disable_functions.filename("core/lib/language_manager.php").function("ALanguageManager>_clone_language_rows").param("from_language").value_r("[^0-9]").drop();
14sp.disable_functions.filename("admin/model/tool/backup.php").function("ModelToolBackup>createBackupTask").param("data[table_list]").value_r("'").drop();
15
16
17# Redaxo 5.2.0: Remote Code Execution via CSRF <https://blog.ripstech.com/2016/redaxo-remote-code-execution-via-csrf>
18# See <http://code.vtiger.com/vtiger/vtigercrm/commit/9b5c5338f80237ae072a06e1ba4a5cfcbfe063b0> for details
19sp.disable_functions.filename("redaxo/src/addons/structure/pages/linkmap.php").function("substr").param("string").value_r("\"").drop();
20
21
22# Guest Post: Vtiger 6.5.0 - SQL Injection <https://blog.ripstech.com/2016/vtiger-sql-injection/>
23sp.disable_functions.filename("modules/Calendar/Activity.php").function("save_module").param("query").value_r("[^0-9;]").drop();
24
25
26# The State of Wordpress Security <https://blog.ripstech.com/2016/the-state-of-wordpress-security>
27# All In One WP Security & Firewall
28sp.disable_functions.filename("admin/wp-security-dashboard-menu.php").function("render_tab3").var("_REQUEST[tab]]").value_r("\"").drop();
29
30
31# PHPKit 1.6.6: Code Execution for Privileged Users <https://blog.ripstech.com/2016/phpkit-code-exection-for-privileged-users>
32sp.disable_functions.filename("pkinc/func/default.php").function("move_uploaded_file").param("destination").value_r("\\.ph\\.+$").drop();
33
34
35# Coppermine 1.5.42: Second-Order Command Execution <https://blog.ripstech.com/2016/coppermine-second-order-command-execution>
36sp.disable_functions.filename("include/imageobject_im.class.php").function("exec").var("CONFIG[im_options]).value_r("[^a-z0-9]").drop();
37sp.disable_functions.filename("forgot_passwd.php").function("cpg_db_query").var("CLEAN[id]").value_r("[^a-z0-9]").drop();
38
39
40# CVE-2014-1610 - Mediawiki RCE
41sp.disable_functions.filename("includes/media/DjVu.php")
42sp.disable_functions.filename("includes/media/ImageHandler.php").var("_GET[page]").value_r("[^0-9]").drop()
43
44
45# CVE-2017-1001000 - https://blog.sucuri.net/2017/02/content-injection-vulnerability-wordpress-rest-api.html
46sp.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();
47sp.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 0000000..6d301b8
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
1snuffleupagus (0.1) UNRELEASED; urgency=medium
2
3 * Initial release.
4
5 -- jvoisin <snuffleupagus@nbs-system.com> Tue, 04 Jul 2017 17:51:31 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..f599e28
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
10
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..c1cfb92
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,14 @@
1Source: snuffleupagus
2Priority: optional
3Maintainer: NBS System <snuffleupagus@nbs-system.com>
4Build-Depends: debhelper (>= 9), php7.0-dev
5Standards-Version: 3.9.2
6Homepage: https://snuffleupagus.fr
7Section: php
8Vcs-Git: https://github.com/nbs-system/snuffleupagus
9
10Package: snuffleupagus
11Architecture: any
12Depends: ${misc:Depends}
13Description: Hardening for your php7 stack
14 Snuffleupagus is cool
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..a792452
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,8 @@
1Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2Upstream-Name: Snuffleupagus
3Upstream-Contact: NBS System <snuffleupagus@nbs-system.com>
4Source: https://github.com/nbs-system/snuffleupagus
5
6Files: *
7Copyright: 2017 NBS System
8License: LGPL
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..b43bf86
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1 @@
README.md
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..66bf44a
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,27 @@
1#!/usr/bin/make -f
2
3DH_VERBOSE = 1
4
5DPKG_EXPORT_BUILDFLAGS = 1
6include /usr/share/dpkg/default.mk
7
8export DEB_BUILD_MAINT_OPTIONS = hardening=+all
9
10%:
11 dh $@
12
13override_dh_auto_clean:
14 cd ./src; phpize --clean
15
16override_dh_auto_configure:
17 cd ./src; phpize
18 cd ./src; ./configure --enable-snuffleupagus
19
20override_dh_auto_build:
21 make -C src
22
23override_dh_auto_install:
24 make -C src install
25
26override_dh_auto_test:
27 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 0000000..86028c7
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,2 @@
1version=3
2https://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 0000000..7355011
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,20 @@
1# Minimal makefile for Sphinx documentation
2#
3
4# You can set these variables from the command line.
5SPHINXOPTS =
6SPHINXBUILD = sphinx-build
7SPHINXPROJ = Snuffleupagus
8SOURCEDIR = source
9BUILDDIR = build
10
11# Put it first so that "make" without argument is like "make help".
12help:
13 @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14
15.PHONY: help Makefile
16
17# Catch-all target: route all unknown targets to Sphinx using the new
18# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19%: Makefile
20 @$(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 0000000..1c47d04
--- /dev/null
+++ b/doc/source/_static/custom.css
@@ -0,0 +1,4 @@
1blockquote {
2 border-left: 2px solid #999;
3 padding-left: 20px;
4} \ 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 0000000..0575ca7
--- /dev/null
+++ b/doc/source/_static/sp.jpg
Binary files differ
diff --git a/doc/source/conf.py b/doc/source/conf.py
new file mode 100644
index 0000000..b2af5f2
--- /dev/null
+++ b/doc/source/conf.py
@@ -0,0 +1,178 @@
1# -*- coding: utf-8 -*-
2#
3# Snuffleupagus documentation build configuration file, created by
4# sphinx-quickstart on Tue Jun 27 14:15:46 2017.
5#
6# This file is execfile()d with the current directory set to its
7# containing dir.
8#
9# Note that not all possible configuration values are present in this
10# autogenerated file.
11#
12# All configuration values have a default; values that are commented out
13# serve to show the default.
14
15# If extensions (or modules to document with autodoc) are in another directory,
16# add these directories to sys.path here. If the directory is relative to the
17# documentation root, use os.path.abspath to make it absolute, like shown here.
18#
19# import os
20# import sys
21# sys.path.insert(0, os.path.abspath('.'))
22from datetime import datetime
23
24
25# -- General configuration ------------------------------------------------
26
27# If your documentation needs a minimal Sphinx version, state it here.
28#
29# needs_sphinx = '1.0'
30
31# Add any Sphinx extension module names here, as strings. They can be
32# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
33# ones.
34#extensions = ['sphinx.ext.githubpages']
35
36# Add any paths that contain templates here, relative to this directory.
37templates_path = ['_templates']
38
39# The suffix(es) of source filenames.
40# You can specify multiple suffix as a list of string:
41#
42# source_suffix = ['.rst', '.md']
43source_suffix = '.rst'
44
45# The master toctree document.
46master_doc = 'index'
47
48# General information about the project.
49project = u'Snuffleupagus'
50copyright = u'%d, NBS System' % datetime.now().year
51author = u'Sebastien Blot & Julien Voisin'
52
53# The version info for the project you're documenting, acts as replacement for
54# |version| and |release|, also used in various other places throughout the
55# built documents.
56#
57# The short X.Y version.
58version = u'0.1'
59# The full version, including alpha/beta/rc tags.
60release = u'Public Alpha'
61
62# The language for content autogenerated by Sphinx. Refer to documentation
63# for a list of supported languages.
64#
65# This is also used if you do content translation via gettext catalogs.
66# Usually you set "language" from the command line for these cases.
67language = None
68
69# List of patterns, relative to source directory, that match files and
70# directories to ignore when looking for source files.
71# This patterns also effect to html_static_path and html_extra_path
72exclude_patterns = []
73
74# The name of the Pygments (syntax highlighting) style to use.
75pygments_style = 'manni'
76
77# If true, `todo` and `todoList` produce output, else they produce nothing.
78todo_include_todos = False
79
80
81# -- Options for HTML output ----------------------------------------------
82
83# The theme to use for HTML and HTML Help pages. See the documentation for
84# a list of builtin themes.
85#
86html_theme = 'alabaster'
87
88# Theme options are theme-specific and customize the look and feel of a theme
89# further. For a list of options available for each theme, see the
90# documentation.
91#
92
93html_sidebars = {
94 '**': [
95 'about.html',
96 'navigation.html',
97 'relations.html',
98 'searchbox.html',
99 #'donate.html',
100 ]
101}
102html_theme_options = {
103 'logo': './sp.jpg',
104 #'description': '<br>Killing bug-classes in PHP 7, virtual-patching the rest.',
105 #'fixed_sidebar': True,
106 'page_width': '60%',
107 'show_powered_by': False,
108 }
109
110sidebar_collapse = True
111
112# Add any paths that contain custom static files (such as style sheets) here,
113# relative to this directory. They are copied after the builtin static files,
114# so a file named "default.css" will overwrite the builtin "default.css".
115html_static_path = ['_static']
116
117#html_logo = './sp.png'
118
119html_show_sphinx = False
120
121# -- Options for HTMLHelp output ------------------------------------------
122
123# Output file base name for HTML help builder.
124htmlhelp_basename = 'Snuffleupagusdoc'
125
126
127# -- Options for LaTeX output ---------------------------------------------
128
129latex_elements = {
130 # The paper size ('letterpaper' or 'a4paper').
131 #
132 # 'papersize': 'letterpaper',
133
134 # The font size ('10pt', '11pt' or '12pt').
135 #
136 # 'pointsize': '10pt',
137
138 # Additional stuff for the LaTeX preamble.
139 #
140 # 'preamble': '',
141
142 # Latex figure (float) alignment
143 #
144 # 'figure_align': 'htbp',
145}
146
147# Grouping the document tree into LaTeX files. List of tuples
148# (source start file, target name, title,
149# author, documentclass [howto, manual, or own class]).
150latex_documents = [
151 (master_doc, 'Snuffleupagus.tex', u'Snuffleupagus Documentation',
152 u'Sebastien Blot \\& Julien Voisin', 'manual'),
153]
154
155
156# -- Options for manual page output ---------------------------------------
157
158# One entry per manual page. List of tuples
159# (source start file, name, description, authors, manual section).
160man_pages = [
161 (master_doc, 'snuffleupagus', u'Snuffleupagus Documentation',
162 [author], 1)
163]
164
165
166# -- Options for Texinfo output -------------------------------------------
167
168# Grouping the document tree into Texinfo files. List of tuples
169# (source start file, target name, title, author,
170# dir menu entry, description, category)
171texinfo_documents = [
172 (master_doc, 'Snuffleupagus', u'Snuffleupagus Documentation',
173 author, 'Snuffleupagus', 'One line description of project.',
174 'Miscellaneous'),
175]
176
177
178
diff --git a/doc/source/config.rst b/doc/source/config.rst
new file mode 100644
index 0000000..8318e7d
--- /dev/null
+++ b/doc/source/config.rst
@@ -0,0 +1,283 @@
1Configuration
2=============
3
4Since PHP *ini-like* configuration model isn't flexible enough,
5Snuffleupagus is using its own format.
6
7Options are chainable by using dots (``.``), and string parameters
8**must** be quoted, while booleans and integers aren't.
9
10Comments are prefixed either with ``#``, or ``;``.
11
12Some rules applies in a specific ``function`` (context), on a specific ``variable``
13(data), like ``disable_functions``, others can only be enabled/disabled, like
14``harden_random``.
15
16.. warning::
17
18 Careful, a wrongly configured Snuffleupagus might break your website.
19 It's up to you to understand its :doc:`features <features>`,
20 read the present documentation about how to configure them,
21 evaluate your threat model, and write your configuration file accordingly.
22
23The rules are evaluated in the order that they are written, and the **first** one
24to match will terminate the evaluation (except for rules in simulation mode).
25
26Bugclass-killer features
27------------------------
28
29global_strict
30^^^^^^^^^^^^^
31`default: disabled`
32
33``global_strict`` will enable the `strict <https://secure.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration.strict>`_ mode globally,
34forcing PHP to throw a `TypeError <https://secure.php.net/manual/en/class.typeerror.php>`_
35exception if an argument type being passed to a function does not match its corresponding declared parameter type.
36
37It can either be ``enabled`` or ``disabled``
38
39::
40
41 sp.global_strict.disable();
42 sp.global_strict.enable();
43
44harden_random
45^^^^^^^^^^^^^
46 * `default: enabled`
47 * `more <features.html#weak-prng-via-rand-mt-rand>`__
48
49``harden_random`` will silently replace the insecure `rand <https://secure.php.net/manual/en/function.rand.php>`_
50and `mt_rand <https://secure.php.net/manual/en/function.mt-rand.php>`_ functions with
51the secure PRNG `random_int <https://secure.php.net/manual/en/function.random-int.php>`_.
52
53It can either be ``enabled`` or ``disabled``.
54
55::
56
57 sp.harden_random.enable();
58 sp.harden_random.disable();
59
60.. _config_global:
61
62global
63^^^^^^
64
65This configuration variable contain parameters that are used by other ones:
66
67- ``secret_key``: A secret key used by various cryptographic features,
68 like `cookies protection <features.html#session-cookie-stealing-via-xss>`__ or `unserialize protection <features.html#unserialize-related-magic>`__,
69 so do make sure that it's random and long enough.
70 You can generate it with something like this: ``head -c 256 /dev/urandom | tr -dc 'a-zA-Z0-9'``.
71
72::
73
74 sp.global.secret_key("44239bd400aa82e125337c9d4eb8315767411ccd");
75
76unserialize_hmac
77^^^^^^^^^^^^^^^^
78 * `default: disabled`
79 * `more <features.html#unserialize-related-magic>`__
80
81``unserialize_hmac`` will add integrity check to ``unserialize`` calls, preventing
82abritrary code execution in their context.
83
84::
85
86 sp.unserialize_hmac.enable();
87 sp.unserialize_hmac.disable();
88
89
90auto_cookie_secure
91^^^^^^^^^^^^^^^^^^
92 * `default: disabled`
93 * `more <features.html#session-cookie-stealing-via-xss>`__
94
95``auto_cookie_secure`` will automatically mark cookies as `secure <https://en.wikipedia.org/wiki/HTTP_cookie#Secure_cookie>`_
96when the web page is requested over HTTPS.
97
98It can either be ``enabled`` or ``disabled``.
99
100::
101
102 sp.auto_cookie_secure.enable();
103 sp.auto_cookie_secure.disable();
104
105cookie_encryption
106^^^^^^^^^^^^^^^^^
107 * `default: disabled`
108 * `more <features.html#session-cookie-stealing-via-xss>`__
109
110.. warning::
111
112 To use this feature, you **must** set the :ref:`global.secret_key <config_global>` variable.
113 This design decision prevents attacker from
114 `trivially bruteforcing <https://www.idontplaydarts.com/2011/11/decrypting-suhosin-sessions-and-cookies/>`_
115 session cookies.
116
117``cookie_secure`` will activate transparent encryption of specific cookies.
118
119It can either be ``enabled`` or ``disabled``.
120
121::
122
123 sp.cookie_encryption.cookie("my_cookie_name");
124 sp.cookie_encryption.cookie("another_cookie_name");
125
126
127readonly_exec
128^^^^^^^^^^^^^
129 * `default: disabled`
130
131``readonly_exec`` will prevent the execution of writable PHP files.
132
133It can either be ``enabled`` or ``disabled``.
134
135::
136
137 sp.readonly_exec.enable();
138
139upload_validation
140^^^^^^^^^^^^^^^^^
141 * `default: disabled`
142 * `more <features.html#remote-code-execution-via-file-upload>`__
143
144``upload_validation`` will call a given script upon a file upload, with the path
145to the file being uploaded as argument, and various information about it in the environment:
146
147* ``SP_FILENAME``: the name of the uploaded file
148* ``SP_FILESIZE``: the size of the file being uploaded
149* ``SP_REMOTE_ADDR``: the ip address of the uploader
150* ``SP_CURRENT_FILE``: the current file being executed
151
152This feature can be used, for example, to check if an uploaded file contains php
153code, with something like `vld <https://derickrethans.nl/projects.html#vld>`_
154(``php -d vld.execute=0 -d vld.active=1 -d extension=vld.so yourfile.php``).
155
156The upload will be **allowed** if the script return the value ``0``. Every other
157value will prevent the file from being uploaded.
158
159::
160
161 sp.upload_validation.script("/var/www/is_valid_php.py").enable();
162
163
164disable_xxe
165^^^^^^^^^^^
166 * `default: enabled`
167 * `more <features.html#xxe>`__
168
169``disable_xxe`` will prevent XXE attacks by disabling the loading of external entities (``libxml_disable_entity_loader``) in the XML parser.
170
171::
172
173 sp.disable_xxe.enable();
174
175
176Virtual-patching
177----------------
178
179Snuffleupagus provides virtual-patching, via the ``disable_functions`` directive, allowing you to stop or control dangerous behaviours.
180Admitting you have a call to ``system()`` that lacks proper user-input validation, thus leading to an **RCE**, this might be the right tool.
181
182::
183
184 # Allow `id.php` to restrict system() calls to `id`
185 sp.disable_functions.function("system").filename("id.php").param("cmd").value("id").allow();
186 sp.disable_functions.function("system").filename("id.php").drop()
187
188Of course, this is a trivial example, and a lot can be achieved with this feature, as you will see below.
189
190
191Filters
192^^^^^^^
193
194- ``alias(:str)``: human-readable description of the rule
195- ``cidr(ip/mask:str)``: match on the client's `cidr <https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing>`_
196- ``filename(name:str)``: match in the file ``name``
197- ``filename_r(regexp:str)``: the file name matching the ``regexp``
198- ``function(name:str)``: match on function ``name``
199- ``function_r(regexp:str)``: the function matching the ``regexp``
200- ``hash(:str)``: match on the file's `sha256 <https://en.wikipedia.org/wiki/SHA-2>`_ sum
201- ``param(name:str)``: match on the function's parameter ``name``
202- ``param_r(regexp:str)``: match on the function's parameter ``regexp``
203- ``param_type(type:str)``: match on the function's parameter ``type``
204- ``ret(value:str)``: match on the function's return ``value``
205- ``ret_r(regexp:str)``: match with a ``regexp`` on the function's return
206- ``ret_type(type_name:str)``: match on the ``type_name`` of the function's return value
207- ``value(:str)``: match on a litteral value
208- ``value_r(:regexp)``: match on a value matching the ``regexp``
209- ``var(name:str)``: match on a **local variable** ``name``
210
211The ``type`` must be one of the following values:
212
213- ``FALSE``: for boolean false
214- ``TRUE``: for boolean true
215- ``NULL``: for the **null** value
216- ``LONG``: for a long (also know as ``integer``) value
217- ``DOUBLE``: for a **double** (also known as ``float``) value
218- ``STRING``: for a string
219- ``OBJECT``: for a object
220- ``ARRAY``: for an array
221- ``RESOURCE``: for a resource
222
223Actions
224^^^^^^^
225
226- ``allow()``: **allow** the request if the rule matches
227- ``drop()``: **drop** the request if the rule matches
228- ``dump(directory:str)``: dump the request in the ``directory`` if it matches the rule
229- ``simulation()``: enabled the simulation mode
230
231Details
232^^^^^^^
233
234The ``function`` filter is able to do various dereferencing:
235
236- ``function("AwesomeClass::my_method")`` will match in the method ``my_method`` in the class ``AwesomeClass``
237
238The ``param`` filter is also able to do some dereferencing:
239
240- ``param(foo[bar])`` will get match on the value corresponding to the ``bar`` key in the hashtable ``foo``.
241 Remember that in PHP, almost every data structure is a hashtable. You can of course nest this like
242 ``param(foo[bar][baz][batman])``.
243- The ``var`` filter will walk the calltrace until it finds the variable's name, or the end of it,
244 allowing to match on global variables: ``.var("_GET[param]")`` will match on the GET parameter ``param``.
245
246For clarity's sake, the presence of the ``allow`` or ``drop`` action is **mandatory**.
247
248.. warning::
249
250 When you're writing rules, please do keep in mind that the **order matters**.
251 For example, if you're denying a call to ``system()`` and then allowing it in a
252 more narrowed way later, the call will be denied,
253 because it'll match the deny first.
254
255If you're paranoid, we're providing a php script to automatically generate
256hash of files containing dangerous functions,
257and blacklisting them everywhere else.
258
259Examples
260^^^^^^^^
261
262Evaluation order of rules
263"""""""""""""""""""""""""
264
265The following rules will:
266
2671. Allow calls to ``system("id")``
2682. Issue a trace in the logs on calls to ``system`` with its parameters starting with ``ping``,
269 and pursuing evaluation of the remaining rules.
2703. Drop calls to ``system``.
271
272
273::
274
275 sp.disable_functions.function("system").param("cmd").value("id").allow();
276 sp.disable_functions.function("system").param("cmd").value_r("^ping").drop().simulation();
277 sp.disable_functions.function("system").param("cmd").drop();
278
279Miscellaneous examples
280""""""""""""""""""""""
281
282.. literalinclude:: ../../config/examples.ini
283 :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 0000000..161937f
--- /dev/null
+++ b/doc/source/download.rst
@@ -0,0 +1,16 @@
1Download
2========
3
4Debian
5------
6
7
8Ubuntu
9------
10
11
12Source code
13-----------
14
15::
16 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 0000000..07aba33
--- /dev/null
+++ b/doc/source/faq.rst
@@ -0,0 +1,196 @@
1FAQ
2===
3
4General
5-------
6
7What is Snuffleupagus?
8""""""""""""""""""""""
9
10Snuffleupagus is a `PHP7+ <http://php.net/manual/en/migration70.php>`_
11module designed to drastically raising the cost of attacks against website,
12by killing entire bug classes, and also providing a powerful virtual-patching system,
13allowing administrator to fix specific vulnerabilities without having to touch the PHP code.
14
15
16Where does the name *Snuffeupagus* comes from?
17""""""""""""""""""""""""""""""""""""""""""""""
18
19 Aloysius Snuffleupagus, more commonly known as Mr. Snuffleupagus, Snuffleupagus
20 or Snuffy for short, is one of the characters on Sesame Street,
21 the educational television program for young children.
22
23 He was created as a woolly mammoth, without tusks or (visible) ears,
24 and has a long thick pointed tail, similar in shape to that of a dinosaur
25 or other reptile. He has long thick brown hair and a trunk, or "snuffle",
26 that drags along the ground. He is Big Bird's best friend and
27 has a baby sister named Alice. He also attends "Snufflegarten".
28
29 --- `Wikipedia <https://en.wikipedia.org/wiki/Mr._Snuffleupagus>`_
30
31
32Why is Snuffleupagus called Snuffleupagus?
33""""""""""""""""""""""""""""""""""""""""""
34
35Like PHP's `ElePHPant <https://secure.php.net/elephpant.php>`_,
36we thought that using an elephant as a mascot would be a great idea.
37
38
39Why did you write Snuffleupagus?
40""""""""""""""""""""""""""""""""
41
42We're working for `NBS System <https://nbs-system.com/en/>`__,
43a web hosting company (meaning that we're dealing with PHP code all day long),
44with a strong focus on security. We do have hardening
45(kernel, `WAF <https://naxsi.org>`_, `IDS <https://en.wikipedia.org/wiki/Intrusion_detection_system>`_, …)
46below the web stack, but most of the time, when a website is compromised,
47it's either to send ads, spam, deface it, steal data, …
48This is why we need to harden the website itself too, but we can't touch its
49source code.
50
51Why not Suhosin?
52""""""""""""""""
53
54We're huge fans of `Suhosin <https://suhosin.org>`_, unfortunately:
55
56- it doesn't work very well on PHP 7
57- it has some oudated features and misses new ones
58- it doesn't cope very well with our various industrialization needs
59- it has some shortcomings by design
60
61We're using the `disable_function <https://secure.php.net/manual/en/ini.core.php#ini.disable-functions>`_
62directive, but unfortunately, it doesn't provide enough usable granularity (guess how many CMS are using
63``system`` to do various mandatory maintenance tasks…).
64
65This is why we decided to write our own hardening module, in the spirit of Suhosin,
66via virtual-patching support, and other cool new features.
67
68What license is Snuffleupagus under and why?
69""""""""""""""""""""""""""""""""""""""""""""
70
71Snuffleupagus is licensed under the `LGPL <https://www.gnu.org/copyleft/lesser.html>`_,
72and is developed by the fine people from `NBS System <https://nbs-system.com/>`__.
73
74We chose the LGPL because we don't care that much how you're using Snuffleupagus,
75but we'd like to force people to make their improvements/contributions
76available to everyone.
77
78Should I use Snuffleupagus?
79"""""""""""""""""""""""""""
80
81Yes.
82
83Even if you're not using the virtual-patching capabilities, Snuffleupagus comes
84with various passive features that won't break your website while killing numerous vulnerabilities.
85
86Please keep in mind that you are not only protecting yourself and your users/customers,
87but also other people on the internet that might be attacked by your server if
88it becomes compromised.
89
90How mature is this project?
91"""""""""""""""""""""""""""
92
93This project was floating around since early 2016, and we did the first commit
94the 28ᵗʰ of December of the same year. We're currently in a private alpha phase,
95finding and fixing as much bugs as possible with the help of friends.
96
97Are you saying that PHP isn't secure?
98"""""""""""""""""""""""""""""""""""""
99
100We don't like PHP's approach of security; namely (sometimes) adding warnings
101in the documentation and trusting the developer to not do any mistake,
102instead of focusing on the root cause, and killing the
103bug class one for all.
104
105Moreover, it seems that the current attitude toward security in the PHP world
106is to `blame the user <https://externals.io/message/100147>`_ instead of acknowledging
107issues, as stated in their `documentation <https://wiki.php.net/security#not_a_security_issue>`_.
108We do think that an security issue that "requires the use of code or settings known to be insecure"
109is still a security issue, and should be treated as such.
110
111Installation and configuration
112------------------------------
113
114Can snuffleupagus break my application?
115"""""""""""""""""""""""""""""""""""""""
116Yes.
117
118Some options won't break anything, like ``harden_rand``, but some like ``global_strict``
119or overly-restrictives virtual-patching rules might pretty well break your website.
120It's up to you to configure Snuffleupaggus accordingly to your needs.
121
122You can also enable the ``simulation`` mode on features that you're not sure about,
123to see what would snuffleupagus do to your application, before activating them for good.
124
125How can I find out the problem when my application breaks?
126""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
127
128By checking the logs; Snuffleupagus systematically prefix them with ``[snuffleupagus]``.
129
130
131Does Snuffleupagus run on Windows?
132""""""""""""""""""""""""""""""""""
133No idea.
134
135
136Will Snuffleupagus run on my old PHP 5?
137"""""""""""""""""""""""""""""""""""""""
138No.
139
140Since PHP5 `will be deprecated at the end of 2018 <http://php.net/supported-versions.php>`_,
141you should think about moving to PHP7 anyway. You can (and should) use
142`Suhosin <https://suhosin.org>`_ in the meantime.
143
144Help and support
145----------------
146
147I found a security issue
148""""""""""""""""""""""""
149If you believe you have found a security issue affecting Snuffleupagus,
150then we would be more than happy to hear from you!
151
152We promise to treat any reported issue seriously and,
153if the investigation confirms it affects Snuffleupagus,
154to patch it within a reasonable time,
155release a public announcement that describes the issue,
156discuss potential impact of the vulnerability,
157reference applicable patches or workarounds,
158and credit the discoverer.
159
160Please send it us a mail to the ``snuffleupagus`` user,
161on ``nbs-system.com``.
162
163I found a bug. How can I report it?
164"""""""""""""""""""""""""""""""""""
165We do have an issue tracker on `Github <https://github.com/nbs-system/snuffleupagus/issues>`_.
166Please make sure to include as much information as possible when reporting your issue,
167such as your operating system, your version of PHP 7, your version of snuffleupagus,
168your logs, the problematic php code, the request, a brief description, … long story short,
169give us everything that you can.
170
171Where can I find even more help?
172""""""""""""""""""""""""""""""""
173The :doc:`configuration page <config>` might be what you're looking for.
174If you're adventurous, you can also check the `issue tracker <https://github.com/nbs-system/snuffleupagus/issues/?q=is%3Aissue>`_
175(make sure to check the closed issues too).
176
177I need professional support for my company.
178"""""""""""""""""""""""""""""""""""""""""""
179Contact `NBS System <https://nbs-system.com>`_.
180
181Unimplemented mitigations and abandoned ideas
182---------------------------------------------
183
184Contant time comparisons
185""""""""""""""""""""""""
186We didn't manage to perform time-based side-channel attacks on strings
187against real world PHP application, and the results that we gathered on
188tailored test cases weren't concluding: for simplicity's sake, we chose
189to not implement a mitigation against this class of attacks.
190
191We would be happy to be proven wrong, and reconsider implementing this feature,
192if someone can manage to get better results than us.
193
194The possibility of having this natively in PHP has
195`been discussed <https://marc.info/?l=php-internals&m=141692988212413&w=2>`_,
196but as 2017, nothing has been merged yet.
diff --git a/doc/source/features.rst b/doc/source/features.rst
new file mode 100644
index 0000000..89cd756
--- /dev/null
+++ b/doc/source/features.rst
@@ -0,0 +1,352 @@
1Features
2========
3
4Snuffleupagus has a lot of features that can be divided in two main categories: bug-classes
5killers and virtual-patching. The first category provides primitives to kill various
6bug families (like arbitrary code execution via ``unserialize`` for example) or rise the
7cost of exploitation, the second one is a highly configurable system to patch functions in php itself.
8
9Bug classes killed
10------------------
11
12``system`` injections
13^^^^^^^^^^^^^^^^^^^^^
14
15The ``system`` function execute an external program and displays the output.
16It's used to interract with various external tools, like file-format converters for example.
17Unfortunately, passing user-controlled parameters to it often leads to an arbitrary command execution.
18
19 When allowing user-supplied data to be passed to this function,
20 use `escapeshellarg()` or `escapeshellcmd()` to ensure that users cannot trick
21 the system into executing arbitrary commands.
22
23 --- `The PHP documentation about system <https://secure.php.net/manual/en/function.system.php>`_
24
25We're kind of killing it by filtering the ``$``, ``|``, ``;``, ````` and ``&`` chars in our
26default configuration, making it a lot harder for an attacker to inject arbitrary commands.
27
28This family of vulnerabilities lead to various CVE, like:
29
30- `CVE-2017-7981 <https://tuleap.net/plugins/tracker/?aid=10159>`_: Authenticated remote code execution on Tuleap
31- `CVE-2014-4688 <https://www.pfsense.org/security/advisories/pfSense-SA-14_10.webgui.asc>`_: Authenticated remote code execution on pfSense
32- `CVE-2014-1610 <https://www.rapid7.com/db/modules/exploit/multi/http/mediawiki_thumb>`_: Unauthenticated remote code execution on DokuWiki
33- `CVE-2013-3630 <https://www.rapid7.com/db/modules/exploit/multi/http/moodle_cmd_exec>`_: Authenticated remote code execution on Moodle
34- Every single shitty `modem/router/switch/IoT <https://twitter.com/internetofshit>`_.
35
36
37``mail``-related injections
38^^^^^^^^^^^^^^^^^^^^^^^^^^^
39
40This vulnerability is known `since 2011 <http://esec-pentest.sogeti.com/posts/2011/11/03/using-mail-for-remote-code-execution.html>`_,
41and was popularized by `RIPS <https://www.ripstech.com/blog/2016/roundcube-command-execution-via-email/>`_ in 2016.
42The last flag of the `mail` function can be used to pass various parameters to
43the underlying binary used to send emails: this can lead to an arbitrary file write,
44often meaning an arbitrary code execution.
45
46 The ``additional_parameters`` parameter can be used to pass additional flags
47 as command line options to the program configured to be used when sending mail
48
49 --- `The PHP documentation about mail <https://secure.php.net/manual/en/function.mail.php>`_
50
51We're killing it by preventing any extra options in additional_parameters.
52
53This family of vulnerabilities lead to various CVE, like:
54
55- `CVE-2017-7692 <https://legalhackers.com/advisories/SquirrelMail-Exploit-Remote-Code-Exec-CVE-2017-7692-Vuln.html>`_: Authenticated remote code execution in SquirrelMail
56- `CVE-2016-10074 <https://legalhackers.com/advisories/SwiftMailer-Exploit-Remote-Code-Exec-CVE-2016-10074-Vuln.html>`_: remote code execution in SwiftMailer
57- `CVE-2016-10033 <https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html>`_: remote code execution in PHPMailer
58- `CVE-2016-9920 <https://www.ripstech.com/blog/2016/roundcube-command-execution-via-email/>`_: Unauthenticated remote code execution in Roundcube
59
60Session-cookie stealing via XSS
61^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
62
63The goto payload for XSS is often to steal cookies.
64Like *Suhosin*, we are encrypting the cookies with a secret key, the IP of the user
65and its user-agent. This means that an attacker with an XSS won't be able to use
66the stolen cookie, since he (often) can't spoof the IP address of the user.
67
68This feature is roughly the same than the `Suhosin one <https://suhosin.org/stories/configuration.html#transparent-encryption-options>`_.
69
70Users behind the same IP address but with different browsers won't be able to use each other stolen cookies,
71except if they can manage to guess the user agent. This isn't especially difficult,
72but an invalid decryption will leave a trace in the logs.
73
74Finally, having a secret server-side key will prevent anyone (even the user himself)
75from reading the content of the cookie, reducing the impact of an application storing sensitive data client-side.
76
77The encryption is done via the [tweetnacl library](https://tweetnacl.cr.yp.to/),
78thus using curve25519, xsalsa20 and poly1305 for the encryption. We chose this
79library because of its portability, simplicity and reduced size (a single `.h` and
80`.c` file.).
81
82Remote code execution via file-upload
83^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
84
85Some PHP applications allows users to upload contents, like avatars for a forum.
86Unfortunately, sometimes, content validation isn't implemented properly (if at all),
87meaning arbitrary file upload, often leading, contrary to what the documentation is saying,
88to an arbitrary code execution.
89
90 Not validating which file you operate on may mean that users can *access sensitive information* in other directories.
91
92 --- `The PHP documentation about file uploads <https://secure.php.net/manual/en/features.file-upload.common-pitfalls.php>`_
93
94We're killing it, like Suhosin, by automatically calling a script upon file upload,
95if it returns something else than ``0``, the file will be removed (or stored in a quarantine,
96for further analysis).
97
98We're recommending to use the `vld <https://derickrethans.nl/projects.html#vld>`_ project
99inside the script to ensure the file doesn't contain any valid PHP code, with something like this:
100
101::
102
103 $ php -d vld.execute=0 -d vld.active=1 -d extension=vld.so $file
104
105Unserialize-related magic
106^^^^^^^^^^^^^^^^^^^^^^^^^
107
108PHP is able to *serialize* arbitrary objects, to easily store them.
109Unfortunately, it's often possible to gain arbitrary code execution upon deserialization
110of user-supplied serialized objects.
111
112 Do not pass untrusted user input to ``unserialize()`` regardless of the options value of allowed_classes.
113 Unserialization can result in code being loaded and executed due to object instantiation and autoloading,
114 and a malicious user may be able to exploit this.
115
116 --- `The PHP documentation about serialize <https://secure.php.net/manual/en/function.serialize.php>`_
117
118We're killing it by exploiting the fact that PHP will discard any garbage found at the end of a serialized object,
119allowing us to simply append a `HMAC <https://en.wikipedia.org/wiki/Hash-based_message_authentication_code>`_
120at the end of strings generated by the ``serialize``,
121hence guaranteeing that any object deserialized came from the application,
122and wasn't tampered with,
123
124We're not encrypting it, like we do with the cookies,
125allowing this feature to be disabled (or switch into leaning mode)
126without the need to invalidate any data.
127
128.. warning::
129
130 This feature can't be deployed on websites that already stored serialized
131 objects (ie. in database), since they are missing the HMAC, and thus will be detected as
132 an attack. If you're in this situation, you should use this feature with the
133 ``simulation`` mode, and switch it off once you don't have any messages in your
134 logs.
135
136A nice side-effect of this feature is that it'll defeat various memory corruption
137issues related to the complexity of ``unserialize``'s implementation,
138and the amount of control if provides to an attacker, like `CVE-2016-9137, CVE-2016-9138 <https://bugs.php.net/bug.php?id=73147>`_,
139`2016-7124 <https://bugs.php.net/bug.php?id=72663>`_, `CVE-2016-5771 and CVE-2016-5773 <https://www.evonide.com/how-we-broke-php-hacked-pornhub-and-earned-20000-dollar/>`_, …
140
141This family of vulnerabilities lead to various CVE, like:
142
143- `CVE-2016-???? <https://www.computest.nl/advisories/CT-2016-1110_Observium.txt>`_: Unauthenticated remote code execution in Observium (leading to remote root)
144- `CVE-2016-5726 <http://seclists.org/oss-sec/2016/q2/521>`_: Unauthenticated remote code execution in Simple Machines Forums
145- `CVE-2016-4010 <http://netanelrub.in/2016/05/17/magento-unauthenticated-remote-code-execution/>`_: Unauthenticated remote code execution in Magento
146- `CVE-2017-2641 <http://netanelrub.in/2017/03/20/moodle-remote-code-execution/>`_: Unauthenticated remote code execution in Moodle
147- `CVE-2015-8562 <https://www.rapid7.com/db/modules/exploit/multi/http/joomla_http_header_rce>`_: Unauthenticated remote code execution in Joomla
148- `CVE-2015-7808 <https://www.rapid7.com/db/modules/exploit/multi/http/vbulletin_unserialize>`_: Unauthenticated remote code execution in vBulletin
149- `CVE-2014-1691 <http://seclists.org/oss-sec/2014/q1/153>`_: Unauthenticated remote code execution in Horde
150- `CVE-2012-5692 <https://www.rapid7.com/db/modules/exploit/unix/webapp/invision_pboard_unserialize_exec>`_: Unauthenticated remote code execution in IP.Board
151
152
153
154Weak-PRNG via rand/mt_rand
155^^^^^^^^^^^^^^^^^^^^^^^^^^
156
157The functions ``rand`` and ``mt_rand`` are often used to generate random numbers used
158in sensitive context, like password generation, token creation, …
159Unfortunately, as said in the documentation, the quality of their entropy is low,
160leading to the generation of guessable values.
161
162 This function does not generate cryptographically secure values, and should not be used for cryptographic purposes.
163
164 --- `The PHP documentation about rand <https://secure.php.net/manual/en/function.rand.php>`_
165
166We're addressing this issue by replacing every call to ``rand`` and ``mt_rand`` with
167a call to the ``random_int``, a `CSPRNG <https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator>`_.
168
169It's worth noting that the PHP documentation contains the following warning:
170
171 ``min`` ``max`` range must be within the range ``getrandmax()``. i.e. ``(max - min) <= getrandmax()``.
172 Otherwise, ``rand()`` may return poor-quality random numbers.
173
174 --- `The PHP documentation about rand <https://secure.php.net/manual/en/function.rand.php>`_
175
176This is of course addressed as well by the ``harden_rand`` feature.
177
178.. warning::
179
180 Activating this feature will raise an `Error <https://secure.php.net/manual/en/class.error.php>`_
181 exception if ``min`` is superior to ``max``, while the default dehaviour is simply to swap them.
182
183This family of vulnerabilities lead to various CVE, like:
184
185- `CVE-2015-5267 <https://moodle.org/mod/forum/discuss.php?d=320291>`_: Unauthenticated accounts takeover in in Moodle
186- `CVE-2014-9624 <https://www.mantisbt.org/bugs/view.php?id=17984>`_: Captcha bypass in MantisBT
187- `CVE-2014-6412 <https://core.trac.wordpress.org/ticket/28633>`_: Unauthenticated account takeover in Wordpress
188- `CVE-2015-???? <https://hackerone.com/reports/31171>`_: Unauthenticated accounts takeover in Concrete5
189- `CVE-2013-6386 <https://www.drupal.org/SA-CORE-2013-003>`_: Unauthenticated accounts takeover in Drupal
190- `CVE-2010-???? <http://www.sektioneins.com/advisories/advisory-022010-mybb-password-reset-weak-random-numbers-vulnerability.html>`_: Unauthenticated accounts takeover in MyBB
191- `CVE-2008-4102 <https://sektioneins.de/en/advisories/advisory-042008-joomla-weak-random-password-reset-token-vulnerability.html>`_: Unauthenticated accounts takeover in Joomla
192- `CVE-2006-0632 <https://www.cvedetails.com/cve/CVE-2006-0632/>`_: Unauthenticated account takeover in phpBB
193
194XXE
195^^^
196
197Despite the documentation saying nothing about this class of vulnerabilities,
198`XML eXternal Entitiy <https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing>`_ (XXE) are often leading to arbitrary file reading, SSRF, and sometimes even arbitrary
199code execution.
200
201XML documents can contain a `Document Type Definition <https://www.w3.org/TR/REC-xml/#sec-prolog-dtd>`_ (DTD),
202enabling definition of XML entities. It's possible to define an (external) entity by an
203URI, that the parser will access, and embed its content back into the document
204for further processing.
205
206For example, providing an url like ``file:///etc/passwd`` will read
207this file's content, and since it's not valid XML, the application
208will spit it out in an error message, thus leaking its content.
209
210We're killing this class of vulnerabilities by calling
211the `libxml_disable_entity_loader <https://secure.php.net/manual/en/function.libxml-disable-entity-loader.php>`_
212function with its parameter set to ``true`` at startup,
213and then *nop'ing* it, so it won't do anything if ever called again.
214
215This family of vulnerabilities lead to various CVE, like:
216
217- `CVE-2015-5161 <https://legalhackers.com/advisories/eBay-Magento-XXE-Injection-Vulnerability.html>`_: Unauthenticated arbitrary file disclosure on Magento
218- `CVE-2014-8790 <https://github.com/GetSimpleCMS/GetSimpleCMS/issues/944>`_: Unauthenticated remote code execution in GetSimple CMS
219- `CVE-2011-4107 <https://www.phpmyadmin.net/security/PMASA-2011-17/>`_: Authenticated local file disclosure in PHPMyAdmin
220
221
222Cookie stealing via HTTP MITM
223^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
224
225While it's possible to set the ``secure`` flag on cookies to prevent them from being
226transmitted over HTTP, and only allow its transmission over HTTPS.
227Snuffleupagus can automatically set this flag if the client is accessing the
228website over a secure connection.
229
230This behaviour is suggested in the documentation:
231
232 On the server-side, it's on the programmer to send this kind of cookie only
233 on secure connection (e.g. with respect to ``$_SERVER["HTTPS"]``).
234
235 --- `The PHP documentation about setcookie <https://secure.php.net/manual/en/function.setcookie.php>`_
236
237
238Exploitation, post-exploitation and general hardening
239-----------------------------------------------------
240
241Virtual-patching
242^^^^^^^^^^^^^^^^
243
244PHP itself exposes a number of functions that might be considered **dangerous** and that have limited legitimate use cases.
245``system()``, ``exec()``, ``dlopen()`` - for example - fall into this category. By default, PHP only allows to globally disable some functions.
246
247
248However, (ie. ``system()``) they might have legitimate use cases in processes such as self upgrade etc., making it impossible to effectively
249disable them - at the risk of breaking critical features.
250
251SnuffleuPagus allows the user to restrict usage of specific functions per files, or per
252files with a matching (sha256) hash, thus allowing the use of such functions **only** in the intended places.
253
254Furthermore, running the `following script <FIXME>`_ will generate an hash and line-based whitelist
255of dangerous functions, droping them everywhere else:
256
257
258.. literalinclude:: ../../scripts/generate_rules.php
259 :language: php
260
261
262The intent is to make post-exploitation process (such as backdooring of legitimate code, or RAT usage) a lot harder for the attacker.
263
264
265Global strict mode
266^^^^^^^^^^^^^^^^^^
267
268By default, PHP will coerce values of the wrong type into the expected one
269if possible. For example, if a function expecting an integer is given a string,
270it will be coerced in an integer.
271
272PHP7 introduced a **strict mode**, in which variables won't be coerced anymore,
273and a `TypeError <https://php.net/manual/en/class.typeerror.php>`_ exception will
274be raised if the types aren't matching.
275`Scalar type declarations <https://secure.php.net/manual/en/migration70.new-features.php#migration70.new-features.scalar-type-declarations>`_
276are optional, but you don't have to used them in your code to benefit from them,
277since every internal function from php has them.
278
279This option provide a switch to globally activate this strict mode,
280helping to uncover vulnerabilities like the classical
281`strcmp bypass <https://danuxx.blogspot.fr/2013/03/unauthorized-access-bypassing-php-strcmp.html>`_,
282and various other types mismatch.
283
284This feature is largely inspired from the
285`autostrict <https://github.com/krakjoe/autostrict>`_ module from `krakjoe <krakjoe.ninja>`_.
286
287
288Preventing execution of writable PHP files
289^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
290
291If an attacker manages to upload an arbitrary file or to modify an existing one,
292odds are that (thanks to the default `umask <https://en.wikipedia.org/wiki/Umask>`_)
293this file is writable by the PHP process.
294
295Snuffleupagus can prevent the execution of this kind of files. A good practise
296would be to use a different user to run PHP than for administrating the website,
297and using this feature to lock this up.
298
299
300
301Dumping capabilities
302^^^^^^^^^^^^^^^^^^^^
303It's possible to apply the ``dump(:str)`` filter to any virtual-patching rule,
304to dump the complete web request, along with the filename and the corresponding
305line number. By using the *right* set of restrictive rules (or by using the
306*overly* restrictives ones in ``simulation`` mode), you might be able
307to gather interesting vulnerabilities used against your website.
308
309
310Misc low-hanging fruits in the default configuration file
311^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
312
313Snuffleupagus is shipping with a default configuration file, containing
314various examples and ideas of things that you might want to enable (or not).
315
316Available functions recon
317"""""""""""""""""""""""""
318
319After compromising a website, most of the time, the attacker does some recon
320within its webshell, to check which functions are available to execute arbitrary code,
321since it's not uncommon for some web-hoster to disable things like ``system`` or ``passthru``,
322or to check if mitigations are enabled, like ``open_basedir``.
323This behaviour can be detected by preventing the execution of functions like ``ini_get``
324or ``is_callable`` with *suspicious* parameters.
325
326``chmod`` hardening
327"""""""""""""""""""
328
329Some PHP applications are using broad rights when using the ``chmod`` function,
330like the infamous ``chmod(777)`` command, effectively making the file writable by everyone.
331Snuffleupagus is preventing this kind of behaviour by restricting the parameters
332than can be passer to ``chmod``.
333
334Arbitrary file inclusion hardening
335""""""""""""""""""""""""""""""""""
336
337Arbitrary file inclusion is a common vulnerability, that might be detected
338by preventing the use of anything else than a whitelist of extensions in calls
339to ``include`` or ``require``.
340
341*Cheap* SQL injections detection
342""""""""""""""""""""""""""""""""
343
344In some SQL injections, attackers might need to use comments, a feature that is
345often not used in production system, so it might be a good idea to filter
346queries that contains some. The same filtering idea can be used against
347SQL functions that are frequently used in SQL injections, like ``sleep``, ``benchmark``
348or strings like ``version_info``.
349
350Still about SQL injections, if a function performing a query returns ``FALSE``
351(indicating an error), it might be useful to dump the request for further analysis.
352
diff --git a/doc/source/index.rst b/doc/source/index.rst
new file mode 100644
index 0000000..8a9c340
--- /dev/null
+++ b/doc/source/index.rst
@@ -0,0 +1,30 @@
1Snuffleupagus
2=============
3
4Snuffleupagus is a `PHP 7+ <https://secure.php.net/>`_ module designed to drastically raising the cost of
5attacks against website, by killing entire bug classes, and also providing a
6powerful virtual-patching system, allowing administrator to fix specific
7vulnerabilities and audit suspicious behaviours without having to touch the PHP code.
8
9Documentation
10-------------
11
12.. toctree::
13 :maxdepth: 2
14
15 features
16 installation
17 config
18 download
19 faq
20 papers
21
22Greetings
23---------
24We would like to thank the following people:
25
26- `Suhosin <http://suhosin.org>`_, for paving the way.
27- The people behind the `RIPS <https://www.ripstech.com/>`_ scanner for their ground breaking work
28- `NBS System <https://nbs-system.com>`_, for creating and open-sourcing this piece of software
29- `Websec.fr <https://websec.fr>`_, for keeping our interesting vulnerabilities alive
30- 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 0000000..779008d
--- /dev/null
+++ b/doc/source/installation.rst
@@ -0,0 +1,33 @@
1Installation
2============
3
4Snuffleupagus is tested against various PHP 7+ versions: XXX
5
6Manual installation
7-------------------
8
9Depending on the system, we might already offer binary packages.
10You can check our :doc:`download`. In that case you only need to activate
11the extension inside your ``php.ini`` and to configure it.
12
13
14Quickstart
15^^^^^^^^^^
16
17::
18
19 git clone https://github.com/nbs-system/snuffleupagus
20 cd snuffleupagus
21 phpize
22 ./configure
23 make
24 make install
25
26This should install ``snuffleupagus.so`` file in your extension directory. The final step is adding a load directive to ``php.ini``::
27
28 extension=snuffleupagus.so
29
30Upgrading
31---------
32
33Upgrading 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 0000000..d028b14
--- /dev/null
+++ b/doc/source/papers.rst
@@ -0,0 +1,16 @@
1Propaganda
2==========
3
4This pages lists various mentions, articles and presentations about Snuffleupagus.
5
6Talks
7-----
8
9- `BerlinSide0x08 <https://berlinsides.org/?page_id=2168>`_ - `Php7 Nightmares <slides.pdf>`_ - 2017-05-28
10- `Hack.lu <https://2017.hack.lu/talks/>`_ - soonâ„¢ - 2017-10-18
11- `BlackAlps <https://blackalps.ch/2017program.php>`_ - soonâ„¢ - 2017-11-16
12
13Articles
14--------
15
16- `Killing php bug classes at berlinsides <https://dustri.org/b/killing-php-bug-classes-at-berlinsides.html>`_ - 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 0000000..e286ef1
--- /dev/null
+++ b/scripts/generate_rules.php
@@ -0,0 +1,43 @@
1<?php
2
3if ($argc != 2) {
4 echo 'Please provide a folder as argument.';
5 die();
6}
7
8$functions_blacklist = ['shell_exec', 'exec', 'passthru', 'php_uname', 'popen',
9 'posix_kill', 'posix_mkfifo', 'posix_setpgid', 'posix_setsid', 'posix_setuid',
10 'posix_setgid', 'posix_uname', 'proc_close', 'proc_nice', 'proc_open',
11 'proc_terminate', 'proc_open', 'proc_get_status', 'dl', 'pnctl_exec',
12 'pnctl_fork', 'assert', 'system'];
13
14$extensions = ['php', 'php7', 'php5'];
15
16$path = realpath($argv[1]);
17
18$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
19foreach($objects as $name => $object){
20 if (FALSE === in_array (pathinfo($name, PATHINFO_EXTENSION), $extensions, true)) {
21 continue;
22 }
23
24 $hash = '';
25 $file_content = file_get_contents($name);
26
27 foreach(token_get_all($file_content) as $token) {
28 if ($token[0] != 319) {
29 continue;
30 }
31
32 if (in_array($token[1], $functions_blacklist, true)) {
33 if ('' === $hash) {
34 $hash = hash('sha256', $file_content);
35 }
36 echo 'sp.disable_function.function("' . $token[1] . '").filename("' . $name . '").hash("' . $hash . '").allow();' . "\n";
37 }
38 }
39}
40foreach($functions_blacklist as $fun) {
41 echo 'sp.disable_function.function("' . $fun . '").drop();' . "\n";
42
43}
diff --git a/src/bench/bench.php b/src/bench/bench.php
new file mode 100644
index 0000000..5f77180
--- /dev/null
+++ b/src/bench/bench.php
@@ -0,0 +1,422 @@
1<?php
2if (function_exists("date_default_timezone_set")) {
3 date_default_timezone_set("UTC");
4}
5
6function simple() {
7 $a = 0;
8 for ($i = 0; $i < 1000000; $i++)
9 $a++;
10
11 $thisisanotherlongname = 0;
12 for ($thisisalongname = 0; $thisisalongname < 1000000; $thisisalongname++)
13 $thisisanotherlongname++;
14}
15
16/****/
17
18function simplecall() {
19 for ($i = 0; $i < 1000000; $i++)
20 strlen("hallo");
21}
22
23/****/
24
25function hallo($a) {
26}
27
28function simpleucall() {
29 for ($i = 0; $i < 1000000; $i++)
30 hallo("hallo");
31}
32
33/****/
34
35function simpleudcall() {
36 for ($i = 0; $i < 1000000; $i++)
37 hallo2("hallo");
38}
39
40function hallo2($a) {
41}
42
43/****/
44
45function mandel() {
46 $w1=50;
47 $h1=150;
48 $recen=-.45;
49 $imcen=0.0;
50 $r=0.7;
51 $s=0; $rec=0; $imc=0; $re=0; $im=0; $re2=0; $im2=0;
52 $x=0; $y=0; $w2=0; $h2=0; $color=0;
53 $s=2*$r/$w1;
54 $w2=40;
55 $h2=12;
56 for ($y=0 ; $y<=$w1; $y=$y+1) {
57 $imc=$s*($y-$h2)+$imcen;
58 for ($x=0 ; $x<=$h1; $x=$x+1) {
59 $rec=$s*($x-$w2)+$recen;
60 $re=$rec;
61 $im=$imc;
62 $color=1000;
63 $re2=$re*$re;
64 $im2=$im*$im;
65 while( ((($re2+$im2)<1000000) && $color>0)) {
66 $im=$re*$im*2+$imc;
67 $re=$re2-$im2+$rec;
68 $re2=$re*$re;
69 $im2=$im*$im;
70 $color=$color-1;
71 }
72 if ( $color==0 ) {
73 print "_";
74 } else {
75 print "#";
76 }
77 }
78 print "<br>";
79 flush();
80 }
81}
82
83/****/
84
85function mandel2() {
86 $b = " .:,;!/>)|&IH%*#";
87 //float r, i, z, Z, t, c, C;
88 for ($y=30; printf("\n"), $C = $y*0.1 - 1.5, $y--;){
89 for ($x=0; $c = $x*0.04 - 2, $z=0, $Z=0, $x++ < 75;){
90 for ($r=$c, $i=$C, $k=0; $t = $z*$z - $Z*$Z + $r, $Z = 2*$z*$Z + $i, $z=$t, $k<5000; $k++)
91 if ($z*$z + $Z*$Z > 500000) break;
92 echo $b[$k%16];
93 }
94 }
95}
96
97/****/
98
99function Ack($m, $n){
100 if($m == 0) return $n+1;
101 if($n == 0) return Ack($m-1, 1);
102 return Ack($m - 1, Ack($m, ($n - 1)));
103}
104
105function ackermann($n) {
106 $r = Ack(3,$n);
107 print "Ack(3,$n): $r\n";
108}
109
110/****/
111
112function ary($n) {
113 for ($i=0; $i<$n; $i++) {
114 $X[$i] = $i;
115 }
116 for ($i=$n-1; $i>=0; $i--) {
117 $Y[$i] = $X[$i];
118 }
119 $last = $n-1;
120 print "$Y[$last]\n";
121}
122
123/****/
124
125function ary2($n) {
126 for ($i=0; $i<$n;) {
127 $X[$i] = $i; ++$i;
128 $X[$i] = $i; ++$i;
129 $X[$i] = $i; ++$i;
130 $X[$i] = $i; ++$i;
131 $X[$i] = $i; ++$i;
132
133 $X[$i] = $i; ++$i;
134 $X[$i] = $i; ++$i;
135 $X[$i] = $i; ++$i;
136 $X[$i] = $i; ++$i;
137 $X[$i] = $i; ++$i;
138 }
139 for ($i=$n-1; $i>=0;) {
140 $Y[$i] = $X[$i]; --$i;
141 $Y[$i] = $X[$i]; --$i;
142 $Y[$i] = $X[$i]; --$i;
143 $Y[$i] = $X[$i]; --$i;
144 $Y[$i] = $X[$i]; --$i;
145
146 $Y[$i] = $X[$i]; --$i;
147 $Y[$i] = $X[$i]; --$i;
148 $Y[$i] = $X[$i]; --$i;
149 $Y[$i] = $X[$i]; --$i;
150 $Y[$i] = $X[$i]; --$i;
151 }
152 $last = $n-1;
153 print "$Y[$last]\n";
154}
155
156/****/
157
158function ary3($n) {
159 for ($i=0; $i<$n; $i++) {
160 $X[$i] = $i + 1;
161 $Y[$i] = 0;
162 }
163 for ($k=0; $k<1000; $k++) {
164 for ($i=$n-1; $i>=0; $i--) {
165 $Y[$i] += $X[$i];
166 }
167 }
168 $last = $n-1;
169 print "$Y[0] $Y[$last]\n";
170}
171
172/****/
173
174function fibo_r($n){
175 return(($n < 2) ? 1 : fibo_r($n - 2) + fibo_r($n - 1));
176}
177
178function fibo($n) {
179 $r = fibo_r($n);
180 print "$r\n";
181}
182
183/****/
184
185function hash1($n) {
186 for ($i = 1; $i <= $n; $i++) {
187 $X[dechex($i)] = $i;
188 }
189 $c = 0;
190 for ($i = $n; $i > 0; $i--) {
191 if ($X[dechex($i)]) { $c++; }
192 }
193 print "$c\n";
194}
195
196/****/
197
198function hash2($n) {
199 for ($i = 0; $i < $n; $i++) {
200 $hash1["foo_$i"] = $i;
201 $hash2["foo_$i"] = 0;
202 }
203 for ($i = $n; $i > 0; $i--) {
204 foreach($hash1 as $key => $value) $hash2[$key] += $value;
205 }
206 $first = "foo_0";
207 $last = "foo_".($n-1);
208 print "$hash1[$first] $hash1[$last] $hash2[$first] $hash2[$last]\n";
209}
210
211/****/
212
213function gen_random ($n) {
214 global $LAST;
215 return( ($n * ($LAST = ($LAST * IA + IC) % IM)) / IM );
216}
217
218function heapsort_r($n, &$ra) {
219 $l = ($n >> 1) + 1;
220 $ir = $n;
221
222 while (1) {
223 if ($l > 1) {
224 $rra = $ra[--$l];
225 } else {
226 $rra = $ra[$ir];
227 $ra[$ir] = $ra[1];
228 if (--$ir == 1) {
229 $ra[1] = $rra;
230 return;
231 }
232 }
233 $i = $l;
234 $j = $l << 1;
235 while ($j <= $ir) {
236 if (($j < $ir) && ($ra[$j] < $ra[$j+1])) {
237 $j++;
238 }
239 if ($rra < $ra[$j]) {
240 $ra[$i] = $ra[$j];
241 $j += ($i = $j);
242 } else {
243 $j = $ir + 1;
244 }
245 }
246 $ra[$i] = $rra;
247 }
248}
249
250function heapsort($N) {
251 global $LAST;
252
253 define("IM", 139968);
254 define("IA", 3877);
255 define("IC", 29573);
256
257 $LAST = 42;
258 for ($i=1; $i<=$N; $i++) {
259 $ary[$i] = gen_random(1);
260 }
261 heapsort_r($N, $ary);
262 printf("%.10f\n", $ary[$N]);
263}
264
265/****/
266
267function mkmatrix ($rows, $cols) {
268 $count = 1;
269 $mx = array();
270 for ($i=0; $i<$rows; $i++) {
271 for ($j=0; $j<$cols; $j++) {
272 $mx[$i][$j] = $count++;
273 }
274 }
275 return($mx);
276}
277
278function mmult ($rows, $cols, $m1, $m2) {
279 $m3 = array();
280 for ($i=0; $i<$rows; $i++) {
281 for ($j=0; $j<$cols; $j++) {
282 $x = 0;
283 for ($k=0; $k<$cols; $k++) {
284 $x += $m1[$i][$k] * $m2[$k][$j];
285 }
286 $m3[$i][$j] = $x;
287 }
288 }
289 return($m3);
290}
291
292function matrix($n) {
293 $SIZE = 30;
294 $m1 = mkmatrix($SIZE, $SIZE);
295 $m2 = mkmatrix($SIZE, $SIZE);
296 while ($n--) {
297 $mm = mmult($SIZE, $SIZE, $m1, $m2);
298 }
299 print "{$mm[0][0]} {$mm[2][3]} {$mm[3][2]} {$mm[4][4]}\n";
300}
301
302/****/
303
304function nestedloop($n) {
305 $x = 0;
306 for ($a=0; $a<$n; $a++)
307 for ($b=0; $b<$n; $b++)
308 for ($c=0; $c<$n; $c++)
309 for ($d=0; $d<$n; $d++)
310 for ($e=0; $e<$n; $e++)
311 for ($f=0; $f<$n; $f++)
312 $x++;
313 print "$x\n";
314}
315
316/****/
317
318function sieve($n) {
319 $count = 0;
320 while ($n-- > 0) {
321 $count = 0;
322 $flags = range (0,8192);
323 for ($i=2; $i<8193; $i++) {
324 if ($flags[$i] > 0) {
325 for ($k=$i+$i; $k <= 8192; $k+=$i) {
326 $flags[$k] = 0;
327 }
328 $count++;
329 }
330 }
331 }
332 print "Count: $count\n";
333}
334
335/****/
336
337function strcat($n) {
338 $str = "";
339 while ($n-- > 0) {
340 $str .= "hello\n";
341 }
342 $len = strlen($str);
343 print "$len\n";
344}
345
346/*****/
347
348function getmicrotime()
349{
350 $t = gettimeofday();
351 return ($t['sec'] + $t['usec'] / 1000000);
352}
353
354function start_test()
355{
356 ob_start();
357 return getmicrotime();
358}
359
360function end_test($start, $name)
361{
362 global $total;
363 $end = getmicrotime();
364 ob_end_clean();
365 $total += $end-$start;
366 $num = number_format($end-$start,3);
367 $pad = str_repeat(" ", 24-strlen($name)-strlen($num));
368
369 echo $name.$pad.$num."\n";
370 ob_start();
371 return getmicrotime();
372}
373
374function total()
375{
376 global $total;
377 $pad = str_repeat("-", 24);
378 echo $pad."\n";
379 $num = number_format($total,3);
380 $pad = str_repeat(" ", 24-strlen("Total")-strlen($num));
381 echo "Total".$pad.$num."\n";
382}
383
384$t0 = $t = start_test();
385simple();
386$t = end_test($t, "simple");
387simplecall();
388$t = end_test($t, "simplecall");
389simpleucall();
390$t = end_test($t, "simpleucall");
391simpleudcall();
392$t = end_test($t, "simpleudcall");
393mandel();
394$t = end_test($t, "mandel");
395mandel2();
396$t = end_test($t, "mandel2");
397ackermann(7);
398$t = end_test($t, "ackermann(7)");
399ary(50000);
400$t = end_test($t, "ary(50000)");
401ary2(50000);
402$t = end_test($t, "ary2(50000)");
403ary3(2000);
404$t = end_test($t, "ary3(2000)");
405fibo(30);
406$t = end_test($t, "fibo(30)");
407hash1(50000);
408$t = end_test($t, "hash1(50000)");
409hash2(500);
410$t = end_test($t, "hash2(500)");
411heapsort(20000);
412$t = end_test($t, "heapsort(20000)");
413matrix(20);
414$t = end_test($t, "matrix(20)");
415nestedloop(12);
416$t = end_test($t, "nestedloop(12)");
417sieve(30);
418$t = end_test($t, "sieve(30)");
419strcat(200000);
420$t = end_test($t, "strcat(200000)");
421total($t0, "Total");
422?>
diff --git a/src/bench/micro_bench.php b/src/bench/micro_bench.php
new file mode 100644
index 0000000..7052588
--- /dev/null
+++ b/src/bench/micro_bench.php
@@ -0,0 +1,358 @@
1<?php
2
3function hallo() {
4}
5
6function simpleucall($n) {
7 for ($i = 0; $i < $n; $i++)
8 hallo();
9}
10
11function simpleudcall($n) {
12 for ($i = 0; $i < $n; $i++)
13 hallo2();
14}
15
16function hallo2() {
17}
18
19function simpleicall($n) {
20 for ($i = 0; $i < $n; $i++)
21 func_num_args();
22}
23
24class Foo {
25 static $a = 0;
26 public $b = 0;
27 const TEST = 0;
28
29 static function read_static($n) {
30 for ($i = 0; $i < $n; ++$i) {
31 $x = self::$a;
32 }
33 }
34
35 static function write_static($n) {
36 for ($i = 0; $i < $n; ++$i) {
37 self::$a = 0;
38 }
39 }
40
41 static function isset_static($n) {
42 for ($i = 0; $i < $n; ++$i) {
43 $x = isset(self::$a);
44 }
45 }
46
47 static function empty_static($n) {
48 for ($i = 0; $i < $n; ++$i) {
49 $x = empty(self::$a);
50 }
51 }
52
53 static function f() {
54 }
55
56 static function call_static($n) {
57 for ($i = 0; $i < $n; ++$i) {
58 self::f();
59 }
60 }
61
62 function read_prop($n) {
63 for ($i = 0; $i < $n; ++$i) {
64 $x = $this->b;
65 }
66 }
67
68 function write_prop($n) {
69 for ($i = 0; $i < $n; ++$i) {
70 $this->b = 0;
71 }
72 }
73
74 function assign_add_prop($n) {
75 for ($i = 0; $i < $n; ++$i) {
76 $this->b += 2;
77 }
78 }
79
80 function pre_inc_prop($n) {
81 for ($i = 0; $i < $n; ++$i) {
82 ++$this->b;
83 }
84 }
85
86 function pre_dec_prop($n) {
87 for ($i = 0; $i < $n; ++$i) {
88 --$this->b;
89 }
90 }
91
92 function post_inc_prop($n) {
93 for ($i = 0; $i < $n; ++$i) {
94 $this->b++;
95 }
96 }
97
98 function post_dec_prop($n) {
99 for ($i = 0; $i < $n; ++$i) {
100 $this->b--;
101 }
102 }
103
104 function isset_prop($n) {
105 for ($i = 0; $i < $n; ++$i) {
106 $x = isset($this->b);
107 }
108 }
109
110 function empty_prop($n) {
111 for ($i = 0; $i < $n; ++$i) {
112 $x = empty($this->b);
113 }
114 }
115
116 function g() {
117 }
118
119 function call($n) {
120 for ($i = 0; $i < $n; ++$i) {
121 $this->g();
122 }
123 }
124
125 function read_const($n) {
126 for ($i = 0; $i < $n; ++$i) {
127 $x = $this::TEST;
128 }
129 }
130
131}
132
133function read_static($n) {
134 for ($i = 0; $i < $n; ++$i) {
135 $x = Foo::$a;
136 }
137}
138
139function write_static($n) {
140 for ($i = 0; $i < $n; ++$i) {
141 Foo::$a = 0;
142 }
143}
144
145function isset_static($n) {
146 for ($i = 0; $i < $n; ++$i) {
147 $x = isset(Foo::$a);
148 }
149}
150
151function empty_static($n) {
152 for ($i = 0; $i < $n; ++$i) {
153 $x = empty(Foo::$a);
154 }
155}
156
157function call_static($n) {
158 for ($i = 0; $i < $n; ++$i) {
159 Foo::f();
160 }
161}
162
163function create_object($n) {
164 for ($i = 0; $i < $n; ++$i) {
165 $x = new Foo();
166 }
167}
168
169define('TEST', null);
170
171function read_const($n) {
172 for ($i = 0; $i < $n; ++$i) {
173 $x = TEST;
174 }
175}
176
177function read_auto_global($n) {
178 for ($i = 0; $i < $n; ++$i) {
179 $x = $_GET;
180 }
181}
182
183$g_var = 0;
184
185function read_global_var($n) {
186 for ($i = 0; $i < $n; ++$i) {
187 $x = $GLOBALS['g_var'];
188 }
189}
190
191function read_hash($n) {
192 $hash = array('test' => 0);
193 for ($i = 0; $i < $n; ++$i) {
194 $x = $hash['test'];
195 }
196}
197
198function read_str_offset($n) {
199 $str = "test";
200 for ($i = 0; $i < $n; ++$i) {
201 $x = $str[1];
202 }
203}
204
205function issetor($n) {
206 $val = array(0,1,2,3,4,5,6,7,8,9);
207 for ($i = 0; $i < $n; ++$i) {
208 $x = $val ?: null;
209 }
210}
211
212function issetor2($n) {
213 $f = false; $j = 0;
214 for ($i = 0; $i < $n; ++$i) {
215 $x = $f ?: $j + 1;
216 }
217}
218
219function ternary($n) {
220 $val = array(0,1,2,3,4,5,6,7,8,9);
221 $f = false;
222 for ($i = 0; $i < $n; ++$i) {
223 $x = $f ? null : $val;
224 }
225}
226
227function ternary2($n) {
228 $f = false; $j = 0;
229 for ($i = 0; $i < $n; ++$i) {
230 $x = $f ? $f : $j + 1;
231 }
232}
233
234/*****/
235
236function empty_loop($n) {
237 for ($i = 0; $i < $n; ++$i) {
238 }
239}
240
241function getmicrotime()
242{
243 $t = gettimeofday();
244 return ($t['sec'] + $t['usec'] / 1000000);
245}
246
247function start_test()
248{
249 ob_start();
250 return getmicrotime();
251}
252
253function end_test($start, $name, $overhead = null)
254{
255 global $total;
256 global $last_time;
257 $end = getmicrotime();
258 ob_end_clean();
259 $last_time = $end-$start;
260 $total += $last_time;
261 $num = number_format($last_time,3);
262 $pad = str_repeat(" ", 24-strlen($name)-strlen($num));
263 if (is_null($overhead)) {
264 echo $name.$pad.$num."\n";
265 } else {
266 $num2 = number_format($last_time - $overhead,3);
267 echo $name.$pad.$num." ".$num2."\n";
268 }
269 ob_start();
270 return getmicrotime();
271}
272
273function total()
274{
275 global $total;
276 $pad = str_repeat("-", 24);
277 echo $pad."\n";
278 $num = number_format($total,3);
279 $pad = str_repeat(" ", 24-strlen("Total")-strlen($num));
280 echo "Total".$pad.$num."\n";
281}
282
283const N = 5000000;
284
285$t0 = $t = start_test();
286empty_loop(N);
287$t = end_test($t, 'empty_loop');
288$overhead = $last_time;
289simpleucall(N);
290$t = end_test($t, 'func()', $overhead);
291simpleudcall(N);
292$t = end_test($t, 'undef_func()', $overhead);
293simpleicall(N);
294$t = end_test($t, 'int_func()', $overhead);
295Foo::read_static(N);
296$t = end_test($t, '$x = self::$x', $overhead);
297Foo::write_static(N);
298$t = end_test($t, 'self::$x = 0', $overhead);
299Foo::isset_static(N);
300$t = end_test($t, 'isset(self::$x)', $overhead);
301Foo::empty_static(N);
302$t = end_test($t, 'empty(self::$x)', $overhead);
303read_static(N);
304$t = end_test($t, '$x = Foo::$x', $overhead);
305write_static(N);
306$t = end_test($t, 'Foo::$x = 0', $overhead);
307isset_static(N);
308$t = end_test($t, 'isset(Foo::$x)', $overhead);
309empty_static(N);
310$t = end_test($t, 'empty(Foo::$x)', $overhead);
311Foo::call_static(N);
312$t = end_test($t, 'self::f()', $overhead);
313call_static(N);
314$t = end_test($t, 'Foo::f()', $overhead);
315$x = new Foo();
316$x->read_prop(N);
317$t = end_test($t, '$x = $this->x', $overhead);
318$x->write_prop(N);
319$t = end_test($t, '$this->x = 0', $overhead);
320$x->assign_add_prop(N);
321$t = end_test($t, '$this->x += 2', $overhead);
322$x->pre_inc_prop(N);
323$t = end_test($t, '++$this->x', $overhead);
324$x->pre_dec_prop(N);
325$t = end_test($t, '--$this->x', $overhead);
326$x->post_inc_prop(N);
327$t = end_test($t, '$this->x++', $overhead);
328$x->post_dec_prop(N);
329$t = end_test($t, '$this->x--', $overhead);
330$x->isset_prop(N);
331$t = end_test($t, 'isset($this->x)', $overhead);
332$x->empty_prop(N);
333$t = end_test($t, 'empty($this->x)', $overhead);
334$x->call(N);
335$t = end_test($t, '$this->f()', $overhead);
336$x->read_const(N);
337$t = end_test($t, '$x = Foo::TEST', $overhead);
338create_object(N);
339$t = end_test($t, 'new Foo()', $overhead);
340read_const(N);
341$t = end_test($t, '$x = TEST', $overhead);
342read_auto_global(N);
343$t = end_test($t, '$x = $_GET', $overhead);
344read_global_var(N);
345$t = end_test($t, '$x = $GLOBALS[\'v\']', $overhead);
346read_hash(N);
347$t = end_test($t, '$x = $hash[\'v\']', $overhead);
348read_str_offset(N);
349$t = end_test($t, '$x = $str[0]', $overhead);
350issetor(N);
351$t = end_test($t, '$x = $a ?: null', $overhead);
352issetor2(N);
353$t = end_test($t, '$x = $f ?: tmp', $overhead);
354ternary(N);
355$t = end_test($t, '$x = $f ? $f : $a', $overhead);
356ternary2(N);
357$t = end_test($t, '$x = $f ? $f : tmp', $overhead);
358total($t0, "Total");
diff --git a/src/config.m4 b/src/config.m4
new file mode 100644
index 0000000..aba355c
--- /dev/null
+++ b/src/config.m4
@@ -0,0 +1,35 @@
1dnl $Id$
2dnl config.m4 for extension snuffleupagus
3
4sources="snuffleupagus.c sp_config.c sp_config_utils.c sp_harden_rand.c"
5sources="$sources sp_unserialize.c sp_utils.c sp_disable_xxe.c sp_list.c"
6sources="$sources sp_disabled_functions.c sp_execute.c sp_upload_validation.c"
7sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c"
8sources="$sources sp_config_keywords.c sp_compile.c"
9
10PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support,
11[ --enable-snuffleupagus Enable snuffleupagus support])
12
13PHP_ARG_ENABLE(coverage, whether to enable coverage support,
14[ --enable-coverage Enable coverage support])
15
16PHP_ARG_ENABLE(debug, whether to enable debug messages,
17[ --enable-debug Enable debug messages])
18
19CFLAGS="$CFLAGS -lpcre"
20CFLAGS="$CFLAGS -D_DEFAULT_SOURCE=1 -std=c99"
21CFLAGS="$CFLAGS -Wall -Wextra -Wno-unused-parameter"
22
23if test "$PHP_DEBUG" = "yes"; then
24 AC_DEFINE(SP_DEBUG, 1, [Wether you want to enable debug messages])
25fi
26
27if test "$PHP_SNUFFLEUPAGUS" != "no"; then
28 if test "$PHP_COVERAGE" != "no"; then
29 CFLAGS="$CFLAGS --coverage -fprofile-arcs -ftest-coverage"
30 LDFLAGS="$LDFLAGS --coverage"
31 PHP_NEW_EXTENSION(snuffleupagus, $sources, $ext_shared,-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -g -fprofile-arcs -ftest-coverage -lgcov)
32 else
33 PHP_NEW_EXTENSION(snuffleupagus, $sources, $ext_shared,-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
34 fi
35fi
diff --git a/src/config.w32 b/src/config.w32
new file mode 100644
index 0000000..a0197c1
--- /dev/null
+++ b/src/config.w32
@@ -0,0 +1,13 @@
1// $Id$
2// vim:ft=javascript
3
4// If your extension references something external, use ARG_WITH
5// ARG_WITH("snuffleupagus", "for snuffleupagus support", "no");
6
7// Otherwise, use ARG_ENABLE
8// ARG_ENABLE("snuffleupagus", "enable snuffleupagus support", "no");
9
10if (PHP_SNUFFLEUPAGUS != "no") {
11 EXTENSION("snuffleupagus", "snuffleupagus.c", PHP_EXTNAME_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
12}
13
diff --git a/src/php_snuffleupagus.h b/src/php_snuffleupagus.h
new file mode 100644
index 0000000..e7a3d59
--- /dev/null
+++ b/src/php_snuffleupagus.h
@@ -0,0 +1,71 @@
1#ifndef PHP_SNUFFLEUPAGUS_H
2#define PHP_SNUFFLEUPAGUS_H
3
4#define PHP_SNUFFLEUPAGUS_VERSION "0.1"
5#define PHP_SNUFFLEUPAGUS_EXTNAME "snuffleupagus"
6#define PHP_SNUFFLEUPAGUS_AUTHOR "NBS System"
7#define PHP_SNUFFLEUPAGUS_URL "https://github.com/nbs-system/snuffleupagus"
8#define PHP_SNUFFLEUPAGUS_COPYRIGHT "LGPLv2"
9
10#include <stdbool.h>
11#include <stdio.h>
12
13#include <pcre.h>
14#include <sys/types.h>
15#include <sys/wait.h>
16
17#include "SAPI.h"
18#include "ext/standard/info.h"
19#include "php.h"
20#include "php_ini.h"
21#include "zend_hash.h"
22#include "zend_string.h"
23#include "zend_extensions.h"
24
25#include "sp_list.h"
26#include "sp_compile.h"
27#include "sp_config.h"
28#include "sp_config_utils.h"
29#include "sp_config_keywords.h"
30#include "sp_cookie_encryption.h"
31#include "sp_disable_xxe.h"
32#include "sp_disabled_functions.h"
33#include "sp_execute.h"
34#include "sp_harden_rand.h"
35#include "sp_network_utils.h"
36#include "sp_unserialize.h"
37#include "sp_upload_validation.h"
38#include "sp_utils.h"
39
40extern zend_module_entry snuffleupagus_module_entry;
41#define phpext_snuffleupagus_ptr &snuffleupagus_module_entry
42
43#ifdef PHP_WIN32
44#define PHP_SNUFFLEUPAGUS_API __declspec(dllexport)
45#elif defined(__GNUC__) && __GNUC__ >= 4
46#define PHP_SNUFFLEUPAGUS_API __attribute__((visibility("default")))
47#else
48#define PHP_SNUFFLEUPAGUS_API
49#endif
50
51#ifdef ZTS
52#include "TSRM.h"
53#endif
54
55ZEND_BEGIN_MODULE_GLOBALS(snuffleupagus)
56sp_config config;
57HashTable *disabled_functions_hook;
58HashTable *sp_internal_functions_hook;
59ZEND_END_MODULE_GLOBALS(snuffleupagus)
60
61#define SNUFFLEUPAGUS_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(snuffleupagus, v)
62
63#if defined(ZTS) && defined(COMPILE_DL_SNUFFLEUPAGUS)
64ZEND_TSRMLS_CACHE_EXTERN()
65#endif
66
67PHP_FUNCTION(check_disabled_function);
68
69static inline void sp_terminate() { zend_bailout(); }
70
71#endif /* PHP_SNUFFLEUPAGUS_H */
diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c
new file mode 100644
index 0000000..52b975e
--- /dev/null
+++ b/src/snuffleupagus.c
@@ -0,0 +1,222 @@
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include "php_snuffleupagus.h"
6
7#ifndef ZEND_EXT_API
8#define ZEND_EXT_API ZEND_DLEXPORT
9#endif
10
11static PHP_INI_MH(OnUpdateConfiguration);
12static inline int zend_auto_start(zend_extension *extension);
13static inline void sp_op_array_handler(zend_op_array *op);
14
15ZEND_EXTENSION();
16
17ZEND_DLEXPORT int sp_zend_startup(zend_extension *extension) {
18 return zend_startup_module(&snuffleupagus_module_entry);
19}
20
21static inline void sp_op_array_handler(zend_op_array *op) {
22 if (NULL == op->filename) {
23 return;
24 } else {
25 op->fn_flags |= ZEND_ACC_STRICT_TYPES;
26 }
27}
28
29ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
30
31PHP_INI_BEGIN()
32PHP_INI_ENTRY("sp.configuration_file", "", PHP_INI_SYSTEM,
33 OnUpdateConfiguration)
34PHP_INI_END()
35
36ZEND_DLEXPORT zend_extension zend_extension_entry = {
37 PHP_SNUFFLEUPAGUS_EXTNAME,
38 PHP_SNUFFLEUPAGUS_VERSION,
39 PHP_SNUFFLEUPAGUS_AUTHOR,
40 PHP_SNUFFLEUPAGUS_URL,
41 PHP_SNUFFLEUPAGUS_COPYRIGHT,
42 sp_zend_startup,
43 NULL,
44 NULL, /* activate_func_t */
45 NULL, /* deactivate_func_t */
46 NULL, /* message_handler_func_t */
47 sp_op_array_handler,//zend_global_strict, /* op_array_handler_func_t */
48 NULL, /* statement_handler_func_t */
49 NULL, /* fcall_begin_handler_func_t */
50 NULL, /* fcall_end_handler_func_t */
51 NULL, /* op_array_ctor_func_t */
52 NULL, /* op_array_dtor_func_t */
53 STANDARD_ZEND_EXTENSION_PROPERTIES};
54
55/* Uncomment this function if you have INI entries
56static void php_snuffleupagus_init_globals(zend_snuffleupagus_globals
57*snuffleupagus_globals)
58{
59 snuffleupagus_globals->global_value = 0;
60 snuffleupagus_globals->global_string = NULL;
61}
62*/
63
64PHP_GINIT_FUNCTION(snuffleupagus) {
65#define SP_INIT(F) F = pecalloc(sizeof(*F), 1, 1);
66#define SP_INIT_HT(F) \
67 F = pemalloc(sizeof(*F), 1); \
68 zend_hash_init(F, 10, NULL, NULL, 1);
69
70 SP_INIT_HT(snuffleupagus_globals->disabled_functions_hook);
71 SP_INIT_HT(snuffleupagus_globals->sp_internal_functions_hook);
72
73 SP_INIT(snuffleupagus_globals->config.config_unserialize);
74 SP_INIT(snuffleupagus_globals->config.config_random);
75 SP_INIT(snuffleupagus_globals->config.config_readonly_exec);
76 SP_INIT(snuffleupagus_globals->config.config_global_strict);
77 SP_INIT(snuffleupagus_globals->config.config_auto_cookie_secure);
78 SP_INIT(snuffleupagus_globals->config.config_snuffleupagus);
79 SP_INIT(snuffleupagus_globals->config.config_disable_xxe);
80 SP_INIT(snuffleupagus_globals->config.config_upload_validation);
81 SP_INIT(snuffleupagus_globals->config.config_disabled_functions);
82 SP_INIT(snuffleupagus_globals->config.config_disabled_functions_ret);
83 SP_INIT(snuffleupagus_globals->config.config_cookie_encryption);
84 SP_INIT(snuffleupagus_globals->config.config_regexp_inclusion);
85
86 snuffleupagus_globals->config.config_regexp_inclusion->regexp_inclusion = sp_new_list();
87 snuffleupagus_globals->config.config_disabled_functions->disabled_functions = sp_new_list();
88 snuffleupagus_globals->config.config_disabled_functions_ret->disabled_functions = sp_new_list();
89
90 SP_INIT_HT(snuffleupagus_globals->config.config_cookie_encryption->names);
91
92#undef SP_INIT
93#undef SP_INIT_HT
94}
95
96PHP_MINIT_FUNCTION(snuffleupagus) {
97 REGISTER_INI_ENTRIES();
98
99 return SUCCESS;
100}
101
102PHP_MSHUTDOWN_FUNCTION(snuffleupagus) {
103#define FREE_HT(F) \
104 zend_hash_destroy(SNUFFLEUPAGUS_G(F)); \
105 pefree(SNUFFLEUPAGUS_G(F), 1);
106
107 FREE_HT(disabled_functions_hook);
108 FREE_HT(config.config_cookie_encryption->names);
109
110#undef FREE_HT
111
112 pefree(SNUFFLEUPAGUS_G(config.config_unserialize), 1);
113 pefree(SNUFFLEUPAGUS_G(config.config_random), 1);
114 pefree(SNUFFLEUPAGUS_G(config.config_readonly_exec), 1);
115 pefree(SNUFFLEUPAGUS_G(config.config_global_strict), 1);
116 pefree(SNUFFLEUPAGUS_G(config.config_auto_cookie_secure), 1);
117 pefree(SNUFFLEUPAGUS_G(config.config_snuffleupagus), 1);
118 pefree(SNUFFLEUPAGUS_G(config.config_disable_xxe), 1);
119 pefree(SNUFFLEUPAGUS_G(config.config_upload_validation), 1);
120 pefree(SNUFFLEUPAGUS_G(config.config_cookie_encryption), 1);
121
122 sp_list_free(SNUFFLEUPAGUS_G(config.config_disabled_functions->disabled_functions));
123 pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions), 1);
124 sp_list_free(SNUFFLEUPAGUS_G(config.config_disabled_functions_ret->disabled_functions));
125 pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions_ret), 1);
126
127 UNREGISTER_INI_ENTRIES();
128
129 return SUCCESS;
130}
131
132PHP_RINIT_FUNCTION(snuffleupagus) {
133#if defined(COMPILE_DL_SNUFFLEUPAGUS) && defined(ZTS)
134 ZEND_TSRMLS_CACHE_UPDATE();
135#endif
136 if (NULL != SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key) {
137 if (NULL != SNUFFLEUPAGUS_G(config).config_cookie_encryption->names) {
138 zend_hash_apply_with_arguments(
139 Z_ARRVAL(PG(http_globals)[TRACK_VARS_COOKIE]), decrypt_cookie, 0);
140 }
141 }
142 return SUCCESS;
143}
144
145PHP_RSHUTDOWN_FUNCTION(snuffleupagus) { return SUCCESS; }
146
147PHP_MINFO_FUNCTION(snuffleupagus) {
148 php_info_print_table_start();
149 php_info_print_table_header(2, "snuffleupagus support", "enabled");
150 php_info_print_table_end();
151
152 /* Remove comments if you have entries in php.ini
153 DISPLAY_INI_ENTRIES();
154 */
155}
156
157static PHP_INI_MH(OnUpdateConfiguration) {
158 TSRMLS_FETCH();
159
160 if (!new_value || !new_value->len) {
161 return FAILURE;
162 }
163
164 if (sp_parse_config(new_value->val) != SUCCESS) {
165 return FAILURE;
166 }
167
168 if (SNUFFLEUPAGUS_G(config).config_random->enable) {
169 hook_rand();
170 }
171 if (SNUFFLEUPAGUS_G(config).config_upload_validation->enable) {
172 hook_upload();
173 }
174 if (SNUFFLEUPAGUS_G(config).config_disable_xxe->enable == 0) {
175 hook_libxml_disable_entity_loader();
176 }
177 hook_disabled_functions();
178 hook_execute();
179
180 if (NULL != SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key) {
181 if (SNUFFLEUPAGUS_G(config).config_unserialize->enable) {
182 hook_serialize();
183 }
184 hook_cookies();
185 }
186
187 if (true == SNUFFLEUPAGUS_G(config).config_global_strict->enable) {
188 if (!zend_get_extension(PHP_SNUFFLEUPAGUS_EXTNAME)) {
189 zend_extension_entry.startup = NULL;
190 zend_register_extension(&zend_extension_entry, NULL);
191 }
192 // This is needed to implement the global strict mode
193 CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
194 }
195
196 return SUCCESS;
197}
198
199const zend_function_entry snuffleupagus_functions[] = {PHP_FE_END};
200
201zend_module_entry snuffleupagus_module_entry =
202 {STANDARD_MODULE_HEADER,
203 PHP_SNUFFLEUPAGUS_EXTNAME,
204 snuffleupagus_functions,
205 PHP_MINIT(snuffleupagus),
206 PHP_MSHUTDOWN(snuffleupagus),
207 PHP_RINIT(snuffleupagus),
208 PHP_RSHUTDOWN(snuffleupagus),
209 PHP_MINFO(snuffleupagus),
210 PHP_SNUFFLEUPAGUS_VERSION,
211 PHP_MODULE_GLOBALS(snuffleupagus),
212 PHP_GINIT(snuffleupagus),
213 NULL,
214 NULL,
215 STANDARD_MODULE_PROPERTIES_EX};
216
217#ifdef COMPILE_DL_SNUFFLEUPAGUS
218#ifdef ZTS
219ZEND_TSRMLS_CACHE_DEFINE()
220#endif
221ZEND_GET_MODULE(snuffleupagus)
222#endif
diff --git a/src/snuffleupagus.php b/src/snuffleupagus.php
new file mode 100644
index 0000000..b373a53
--- /dev/null
+++ b/src/snuffleupagus.php
@@ -0,0 +1,21 @@
1<?php
2$br = (php_sapi_name() == "cli")? "":"<br>";
3
4if(!extension_loaded('snuffleupagus')) {
5 dl('snuffleupagus.' . PHP_SHLIB_SUFFIX);
6}
7$module = 'snuffleupagus';
8$functions = get_extension_funcs($module);
9echo "Functions available in the test extension:$br\n";
10foreach($functions as $func) {
11 echo $func."$br\n";
12}
13echo "$br\n";
14$function = 'confirm_' . $module . '_compiled';
15if (extension_loaded($module)) {
16 $str = $function($module);
17} else {
18 $str = "Module $module is not compiled into PHP";
19}
20echo "$str\n";
21?>
diff --git a/src/sp_compile.c b/src/sp_compile.c
new file mode 100644
index 0000000..2902377
--- /dev/null
+++ b/src/sp_compile.c
@@ -0,0 +1,53 @@
1#include "php_snuffleupagus.h"
2
3ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus);
4
5static zend_op_array *(*orig_compile_file)(zend_file_handle *, int);
6static zend_op_array *(*orig_compile_string)(zval *, char *);
7
8zend_op_array *sp_compile_file(zend_file_handle *file_handle, int type) {
9 zend_op_array *ret = orig_compile_file(file_handle, type);
10
11 const sp_node_t* config = SNUFFLEUPAGUS_G(config).config_disabled_functions->disabled_functions;
12
13 while (config && config->data) {
14 const char* function_name = ((sp_disabled_function*)config->data)->function;
15 const pcre* function_name_regexp = ((sp_disabled_function*)config->data)->r_function;
16
17 sp_log_err("checking for %s", function_name);
18 // EG(function_table)->arData[count - 1].val.value.func->internal_function.handler = PHP_FN(check_disabled_function);
19
20 if (function_name) {
21 if (HOOK_FUNCTION(function_name, disabled_functions_hook, PHP_FN(check_disabled_function), true) == SUCCESS) {
22 sp_log_err("Successfully hooked %s", function_name);
23 }
24 break;
25 } else if (function_name_regexp) {
26 sp_log_err("error", "We'll hook regard later.");
27 }
28 config = config->next;
29 }
30
31 return ret;
32}
33
34zend_op_array *sp_compile_string(zval *source_string, char *filename) {
35 zend_op_array *ret = orig_compile_string(source_string, filename);
36 sp_log_err("in compile_string : filename is :%s", filename);
37 return ret;
38}
39
40
41int hook_compile(void) {
42 TSRMLS_FETCH();
43
44 /* zend_compile_file is used to compile php file */
45 orig_compile_file = zend_compile_file;
46 zend_compile_file = sp_compile_file;
47
48 /* zend_compile_string is used to compile php string */
49 orig_compile_string = zend_compile_string;
50 zend_compile_string = sp_compile_string;
51
52 return SUCCESS;
53} \ No newline at end of file
diff --git a/src/sp_compile.h b/src/sp_compile.h
new file mode 100644
index 0000000..538ff52
--- /dev/null
+++ b/src/sp_compile.h
@@ -0,0 +1,6 @@
1#ifndef SP_COMPILE_H
2#define SP_COMPILE_H
3
4int hook_compile(void);
5
6#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 0000000..f73347d
--- /dev/null
+++ b/src/sp_config.c
@@ -0,0 +1,193 @@
1#include <errno.h>
2#include <stdio.h>
3#include <string.h>
4
5#include "php_snuffleupagus.h"
6
7ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
8
9sp_config_tokens const sp_func[] = {
10 {.func = parse_unserialize, .token = SP_TOKEN_UNSERIALIZE_HMAC},
11 {.func = parse_random, .token = SP_TOKEN_HARDEN_RANDOM},
12 {.func = parse_disabled_functions, .token = SP_TOKEN_DISABLE_FUNC},
13 {.func = parse_readonly_exec, .token = SP_TOKEN_READONLY_EXEC},
14 {.func = parse_global_strict, .token = SP_TOKEN_GLOBAL_STRICT},
15 {.func = parse_upload_validation, .token = SP_TOKEN_UPLOAD_VALIDATION},
16 {.func = parse_cookie_encryption, .token = SP_TOKEN_COOKIE_ENCRYPTION},
17 {.func = parse_global, .token = SP_TOKEN_GLOBAL},
18 {.func = parse_auto_cookie_secure, .token = SP_TOKEN_AUTO_COOKIE_SECURE},
19 {.func = parse_disable_xxe, .token = SP_TOKEN_DISABLE_XXE},
20 {NULL, NULL}};
21
22/* Top level keyword parsing */
23
24static int parse_line(char *line) {
25 char *ptr = line;
26
27 while (*ptr == ' ' || *ptr == '\t') {
28 ++ptr;
29 }
30
31 if (!*ptr || *ptr == '#' || *ptr == ';') {
32 return 0;
33 }
34
35 if (strncmp(ptr, SP_TOKEN_BASE, strlen(SP_TOKEN_BASE))) {
36 sp_log_err("config", "Invalid configuration prefix for '%s'.", line);
37 return -1;
38 }
39 ptr += strlen(SP_TOKEN_BASE);
40
41 for (size_t i = 0; sp_func[i].func; i++) {
42 if (!strncmp(sp_func[i].token, ptr, strlen(sp_func[i].token))) {
43 return sp_func[i].func(ptr + strlen(sp_func[i].token));
44 }
45 }
46 sp_log_err("config", "Invalid configuration section '%s'.", line);
47 return -1;
48}
49
50/* keyword parsing */
51int parse_empty(char *restrict line, char *restrict keyword, void *retval) {
52 *(bool *)retval = true;
53 return 0;
54}
55
56int parse_int(char *restrict line, char *restrict keyword, void *retval) {
57 size_t consumed = 0;
58 char *value = get_param(&consumed, line, SP_TYPE_INT, keyword);
59 if (value) {
60 sscanf(value, "%ud", (uint32_t *)retval);
61 pefree(value, 1);
62 return consumed;
63 } else {
64 sp_log_err("error", "%s) is expecting a valid integer.", keyword);
65 return -1;
66 }
67}
68
69int parse_php_type(char *restrict line, char *restrict keyword, void *retval) {
70 size_t consumed = 0;
71 char *value = get_param(&consumed, line, SP_TYPE_STR, keyword);
72 if (value) {
73 if (0 == strcasecmp("undef", value)) {
74 *(sp_php_type*)retval = SP_PHP_TYPE_UNDEF;
75 } else if (0 == strcasecmp("null", value)) {
76 *(sp_php_type*)retval = SP_PHP_TYPE_NULL;
77 } else if (0 == strcasecmp("true", value)) {
78 *(sp_php_type*)retval = SP_PHP_TYPE_TRUE;
79 } else if (0 == strcasecmp("false", value)) {
80 *(sp_php_type*)retval = SP_PHP_TYPE_FALSE;
81 } else if (0 == strcasecmp("long", value)) {
82 *(sp_php_type*)retval = SP_PHP_TYPE_LONG;
83 } else if (0 == strcasecmp("double", value)) {
84 *(sp_php_type*)retval = SP_PHP_TYPE_DOUBLE;
85 } else if (0 == strcasecmp("string", value)) {
86 *(sp_php_type*)retval = SP_PHP_TYPE_STRING;
87 } else if (0 == strcasecmp("array", value)) {
88 *(sp_php_type*)retval = SP_PHP_TYPE_ARRAY;
89 } else if (0 == strcasecmp("object", value)) {
90 *(sp_php_type*)retval = SP_PHP_TYPE_OBJECT;
91 } else if (0 == strcasecmp("resource", value)) {
92 *(sp_php_type*)retval = SP_PHP_TYPE_RESOURCE;
93 } else if (0 == strcasecmp("reference", value)) {
94 *(sp_php_type*)retval = SP_PHP_TYPE_REFERENCE;
95 } else {
96 pefree(value, 1);
97 sp_log_err("error", "%s) is expecting a valid php type ('false', 'true',"
98 " 'array'. 'object', 'long', 'double', 'null', 'resource', 'reference',"
99 " 'undef').", keyword);
100 return -1;
101 }
102 pefree(value, 1);
103 return consumed;
104 } else {
105 return -1;
106 }
107}
108
109int parse_str(char *restrict line, char *restrict keyword, void *retval) {
110 char *value = NULL;
111
112 size_t consumed = 0;
113 value = get_param(&consumed, line, SP_TYPE_STR, keyword);
114 if (value) {
115 *(char **)retval = value;
116 return consumed;
117 }
118 return -1;
119}
120
121int parse_cidr(char *restrict line, char *restrict keyword, void *retval) {
122 size_t consumed = 0;
123 char *value = get_param(&consumed, line, SP_TYPE_STR, keyword);
124 sp_cidr *cidr = pecalloc(sizeof(sp_cidr), 1, 1);
125
126 if (value) {
127 if (-1 == get_ip_and_cidr(value, cidr)) {
128 return -1;
129 }
130 *(sp_cidr **)retval = cidr;
131 return consumed;
132 } else {
133 sp_log_err("config", "%s doesn't contain a valid cidr.", line);
134 return -1;
135 }
136}
137
138int parse_regexp(char *restrict line, char *restrict keyword, void *retval) {
139 /* TODO: Do we want to use pcre_study?
140 * (http://www.pcre.org/original/doc/html/pcre_study.html)
141 * maybe not: http://sljit.sourceforge.net/pcre.html*/
142 size_t consumed = 0;
143 char *value = get_param(&consumed, line, SP_TYPE_STR, keyword);
144
145 if (value) {
146 const char *pcre_error;
147 int pcre_error_offset;
148 pcre *compiled_re = pcre_compile(value, PCRE_CASELESS, &pcre_error,
149 &pcre_error_offset, NULL);
150 if (NULL == compiled_re) {
151 sp_log_err("config", "Failed to compile '%s': %s.", value, pcre_error);
152 } else {
153 *(pcre **)retval = compiled_re;
154 return consumed;
155 }
156 }
157 char *closing_paren = strchr(line, ')');
158 if (NULL != closing_paren) {
159 closing_paren[0] = '\0';
160 }
161 sp_log_err("config", "'%s)' is expecting a valid regexp, and not '%s'.",
162 keyword, line);
163 return -1;
164}
165
166int sp_parse_config(const char *conf_file) {
167 FILE *fd = fopen(conf_file, "r");
168 char *lineptr = NULL;
169 size_t n = 0;
170
171 if (fd == NULL) {
172 sp_log_err("config", "Could not open configuration file %s : %s", conf_file,
173 strerror(errno));
174 return FAILURE;
175 }
176
177 while (getline(&lineptr, &n, fd) > 0) {
178 /* We trash the terminal `\n`. This simplify the display of logs. */
179 if (lineptr[strlen(lineptr) - 1] == '\n') {
180 lineptr[strlen(lineptr) - 1] = '\0';
181 }
182 if (parse_line(lineptr) == -1) {
183 fclose(fd);
184 free(lineptr);
185 return FAILURE;
186 }
187 free(lineptr);
188 lineptr = NULL;
189 n = 0;
190 }
191 fclose(fd);
192 return SUCCESS;
193}
diff --git a/src/sp_config.h b/src/sp_config.h
new file mode 100644
index 0000000..54ec2cc
--- /dev/null
+++ b/src/sp_config.h
@@ -0,0 +1,206 @@
1#ifndef SP_CONFIG_H
2#define SP_CONFIG_H
3
4#include <arpa/inet.h>
5#include <netinet/in.h>
6#include <sys/socket.h>
7
8typedef enum {
9 SP_TYPE_STR = 0,
10 SP_TYPE_REGEXP,
11 SP_TYPE_INT,
12 SP_TYPE_EMPTY
13} sp_type;
14
15typedef enum {
16 SP_PHP_TYPE_UNDEF = IS_UNDEF,
17 SP_PHP_TYPE_NULL = IS_NULL,
18 SP_PHP_TYPE_FALSE = IS_FALSE,
19 SP_PHP_TYPE_TRUE = IS_TRUE,
20 SP_PHP_TYPE_LONG = IS_LONG,
21 SP_PHP_TYPE_DOUBLE = IS_DOUBLE,
22 SP_PHP_TYPE_STRING = IS_STRING,
23 SP_PHP_TYPE_ARRAY = IS_ARRAY,
24 SP_PHP_TYPE_OBJECT = IS_OBJECT,
25 SP_PHP_TYPE_RESOURCE = IS_RESOURCE,
26 SP_PHP_TYPE_REFERENCE = IS_REFERENCE
27} sp_php_type;
28
29typedef struct {
30 int ip_version;
31 union {
32 struct in_addr ipv4;
33 struct in6_addr ipv6;
34 } ip;
35 uint8_t mask;
36} sp_cidr;
37
38typedef struct { char *encryption_key; } sp_config_encryption_key;
39
40typedef struct {
41 bool enable;
42 bool simulation;
43} sp_config_readonly_exec;
44
45typedef struct { bool enable; } sp_config_global_strict;
46
47typedef struct { bool enable; } sp_config_random;
48
49typedef struct { bool enable; } sp_config_auto_cookie_secure;
50
51typedef struct { bool enable; } sp_config_disable_xxe;
52
53typedef struct {
54 HashTable *names;
55 uint32_t mask_ipv4;
56 uint32_t mask_ipv6;
57} sp_config_cookie_encryption;
58
59typedef struct {
60 bool enable;
61 bool simulation;
62} sp_config_unserialize;
63
64typedef struct {
65 char *filename;
66 pcre *r_filename;
67
68 char *function;
69 pcre *r_function;
70
71 char *hash;
72 int simulation;
73 bool enable;
74
75 char *param;
76 pcre *r_param;
77 sp_php_type param_type;
78
79 char *ret;
80 pcre *r_ret;
81 sp_php_type ret_type;
82
83 pcre *regexp;
84 char *value;
85
86 char *dump;
87 char *alias;
88 bool param_is_array;
89 bool var_is_array;
90 sp_node_t *param_array_keys;
91 sp_node_t *var_array_keys;
92
93 bool allow;
94 bool drop;
95
96 char *var;
97
98 sp_cidr *cidr;
99} sp_disabled_function;
100
101typedef struct {
102 sp_node_t *disabled_functions; // list of sp_disabled_function
103} sp_config_disabled_functions;
104
105typedef struct {
106 sp_node_t *regexp_inclusion; // list of regexp for inclusion
107} sp_config_regexp_inclusion;
108
109typedef struct {
110 char *script;
111 bool simulation;
112 bool enable;
113} sp_config_upload_validation;
114
115typedef struct {
116 sp_config_random *config_random;
117 sp_config_unserialize *config_unserialize;
118 sp_config_disabled_functions *config_disabled_functions;
119 sp_config_disabled_functions *config_disabled_functions_ret;
120 sp_config_readonly_exec *config_readonly_exec;
121 sp_config_upload_validation *config_upload_validation;
122 sp_config_cookie_encryption *config_cookie_encryption;
123 sp_config_encryption_key *config_snuffleupagus;
124 sp_config_auto_cookie_secure *config_auto_cookie_secure;
125 sp_config_global_strict *config_global_strict;
126 sp_config_disable_xxe *config_disable_xxe;
127 sp_config_regexp_inclusion *config_regexp_inclusion;
128} sp_config;
129
130typedef struct {
131 int (*func)(char *, char *, void *);
132 char *token;
133 void *retval;
134} sp_config_functions;
135
136typedef struct {
137 int (*func)(char *);
138 char *token;
139} sp_config_tokens;
140
141#define SP_TOKEN_BASE "sp"
142
143#define SP_TOKEN_AUTO_COOKIE_SECURE ".auto_cookie_secure"
144#define SP_TOKEN_COOKIE_ENCRYPTION ".cookie_encryption"
145#define SP_TOKEN_DISABLE_FUNC ".disable_functions"
146#define SP_TOKEN_GLOBAL ".global"
147#define SP_TOKEN_GLOBAL_STRICT ".global_strict"
148#define SP_TOKEN_HARDEN_RANDOM ".harden_random"
149#define SP_TOKEN_READONLY_EXEC ".readonly_exec"
150#define SP_TOKEN_UNSERIALIZE_HMAC ".unserialize_hmac"
151#define SP_TOKEN_UPLOAD_VALIDATION ".upload_validation"
152#define SP_TOKEN_DISABLE_XXE ".disable_xxe"
153
154// common tokens
155#define SP_TOKEN_ENABLE ".enable("
156#define SP_TOKEN_DISABLE ".disable("
157#define SP_TOKEN_SIMULATION ".simulation("
158#define SP_TOKEN_TRUE "1"
159#define SP_TOKEN_FALSE "0"
160#define SP_TOKEN_DUMP ".dump("
161#define SP_TOKEN_ALIAS ".alias("
162#define SP_TOKEN_ALLOW ".allow("
163#define SP_TOKEN_DROP ".drop("
164
165#define SP_TOKEN_END_PARAM ')'
166
167// disable_function
168#define SP_TOKEN_CIDR ".cidr("
169#define SP_TOKEN_FILENAME ".filename("
170#define SP_TOKEN_FILENAME_REGEXP ".filename_r("
171#define SP_TOKEN_FUNCTION ".function("
172#define SP_TOKEN_FUNCTION_REGEXP ".function_r("
173#define SP_TOKEN_HASH ".hash("
174#define SP_TOKEN_LOCAL_VAR ".var("
175#define SP_TOKEN_PARAM ".param("
176#define SP_TOKEN_PARAM_REGEXP ".param_r("
177#define SP_TOKEN_PARAM_TYPE ".param_type("
178#define SP_TOKEN_RET ".ret("
179#define SP_TOKEN_RET_REGEXP ".ret_r("
180#define SP_TOKEN_RET_TYPE ".ret_type("
181#define SP_TOKEN_VALUE ".value("
182#define SP_TOKEN_VALUE_REGEXP ".value_r("
183
184// cookies encryption
185#define SP_TOKEN_NAME ".cookie("
186#define SP_TOKEN_MASK_IPV4 ".mask_ipv4("
187#define SP_TOKEN_MASK_IPV6 ".mask_ipv6("
188
189// Global configuration options
190#define SP_TOKEN_ENCRYPTION_KEY ".secret_key("
191
192// upload_validator
193#define SP_TOKEN_UPLOAD_SCRIPT ".script("
194
195int sp_parse_config(const char *);
196int parse_array(sp_disabled_function *);
197
198int parse_str(char *restrict, char *restrict, void *);
199int parse_regexp(char *restrict, char *restrict, void *);
200int parse_empty(char *restrict, char *restrict, void *);
201int parse_int(char *restrict, char *restrict, void *);
202int parse_cidr(char *restrict, char *restrict, void *);
203int parse_php_type(char *restrict, char *restrict, void *);
204
205
206#endif /* SP_CONFIG_H */
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c
new file mode 100644
index 0000000..4a6dd3a
--- /dev/null
+++ b/src/sp_config_keywords.c
@@ -0,0 +1,268 @@
1#include "php_snuffleupagus.h"
2
3ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
4
5static int parse_enable(char *line, bool * restrict retval, bool * restrict simulation) {
6 bool enable = false, disable = false;
7 sp_config_functions sp_config_funcs[] = {
8 {parse_empty, SP_TOKEN_ENABLE, &(enable)},
9 {parse_empty, SP_TOKEN_DISABLE, &(disable)},
10 {parse_empty, SP_TOKEN_SIMULATION, simulation},
11 {0}};
12
13 int ret = parse_keywords(sp_config_funcs, line);
14
15 if (0 != ret) {
16 return ret;
17 }
18
19 if (!(enable ^ disable)) {
20 sp_log_err("config", "A rule can't be enabled and disabled.");
21 return -1;
22 }
23
24 *retval = enable;
25
26 return ret;
27}
28
29int parse_random(char *line) {
30 return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_random->enable), NULL);
31}
32
33int parse_disable_xxe(char *line) {
34 return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_disable_xxe->enable), NULL);
35}
36
37int parse_auto_cookie_secure(char *line) {
38 return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_auto_cookie_secure->enable), NULL);
39}
40
41int parse_global_strict(char *line) {
42 return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_global_strict->enable), NULL);
43}
44
45int parse_unserialize(char *line) {
46 return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_unserialize->enable), &(SNUFFLEUPAGUS_G(config).config_unserialize->simulation));
47}
48
49int parse_readonly_exec(char *line) {
50 return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_readonly_exec->enable), &(SNUFFLEUPAGUS_G(config).config_readonly_exec->simulation));
51}
52
53int parse_global(char *line) {
54 sp_config_functions sp_config_funcs_encryption_key[] = {
55 {parse_str, SP_TOKEN_ENCRYPTION_KEY,
56 &(SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key)},
57 {0}};
58 return parse_keywords(sp_config_funcs_encryption_key, line);
59}
60
61int parse_cookie_encryption(char *line) {
62 int ret = 0;
63 char *name = NULL;
64
65 sp_config_functions sp_config_funcs_cookie_encryption[] = {
66 {parse_str, SP_TOKEN_NAME, &name},
67 {parse_int, SP_TOKEN_MASK_IPV4,
68 &(SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv4)},
69 {parse_int, SP_TOKEN_MASK_IPV6,
70 &(SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv6)},
71 {0}};
72
73 ret = parse_keywords(sp_config_funcs_cookie_encryption, line);
74 if (0 != ret) {
75 return ret;
76 }
77
78 if (32 < SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv4) {
79 SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv4 = 32;
80 }
81 if (128 < SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv6) {
82 SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv6 = 128;
83 }
84
85 if (name) {
86 zend_hash_str_add_empty_element(
87 SNUFFLEUPAGUS_G(config).config_cookie_encryption->names, name,
88 strlen(name));
89 }
90 return SUCCESS;
91}
92
93int parse_disabled_functions(char *line) {
94 int ret = 0;
95 bool enable = true, disable = false;
96 sp_disabled_function *df = pecalloc(sizeof(*df), 1, 1);
97
98 sp_config_functions sp_config_funcs_disabled_functions[] = {
99 {parse_empty, SP_TOKEN_ENABLE, &(enable)},
100 {parse_empty, SP_TOKEN_DISABLE, &(disable)},
101 {parse_str, SP_TOKEN_ALIAS, &(df->alias)},
102 {parse_empty, SP_TOKEN_SIMULATION, &(df->simulation)},
103 {parse_str, SP_TOKEN_FILENAME, &(df->filename)},
104 {parse_regexp, SP_TOKEN_FILENAME_REGEXP, &(df->r_filename)},
105 {parse_str, SP_TOKEN_FUNCTION, &(df->function)},
106 {parse_regexp, SP_TOKEN_FUNCTION_REGEXP, &(df->r_function)},
107 {parse_str, SP_TOKEN_DUMP, &(df->dump)},
108 {parse_empty, SP_TOKEN_ALLOW, &(df->allow)},
109 {parse_empty, SP_TOKEN_DROP, &(df->drop)},
110 {parse_str, SP_TOKEN_HASH, &(df->hash)},
111 {parse_str, SP_TOKEN_PARAM, &(df->param)},
112 {parse_regexp, SP_TOKEN_VALUE_REGEXP, &(df->regexp)},
113 {parse_str, SP_TOKEN_VALUE, &(df->value)},
114 {parse_regexp, SP_TOKEN_PARAM_REGEXP, &(df->r_param)},
115 {parse_php_type, SP_TOKEN_PARAM_TYPE, &(df->param_type)},
116 {parse_str, SP_TOKEN_RET, &(df->ret)},
117 {parse_cidr, SP_TOKEN_CIDR, &(df->cidr)},
118 {parse_regexp, SP_TOKEN_RET_REGEXP, &(df->r_ret)},
119 {parse_php_type, SP_TOKEN_RET_TYPE, &(df->ret_type)},
120 {parse_str, SP_TOKEN_LOCAL_VAR, &(df->var)},
121 {0}};
122
123 ret = parse_keywords(sp_config_funcs_disabled_functions, line);
124
125 if (0 != ret) {
126 return ret;
127 }
128
129 if (true == disable){
130 df->enable = false;
131 } else {
132 df->enable = true;
133 }
134
135 if (df->value && df->regexp) {
136 sp_log_err("config",
137 "Invalid configuration line: 'sp.disabled_functions%s':"
138 "'.value' and '.regexp' are mutually exclusives.",
139 line);
140 return -1;
141 } else if (df->r_function && df->function) {
142 sp_log_err("config",
143 "Invalid configuration line: 'sp.disabled_functions%s': "
144 "'.r_function' and '.function' are mutually exclusive.",
145 line);
146 return -1;
147 } else if (df->r_filename && df->filename) {
148 sp_log_err("config",
149 "Invalid configuration line: 'sp.disabled_functions%s':"
150 "'.r_filename' and '.filename' are mutually exclusive.",
151 line);
152 return -1;
153 } else if (df->r_param && df->param) {
154 sp_log_err("config",
155 "Invalid configuration line: 'sp.disabled_functions%s':"
156 "'.r_param' and '.param' are mutually exclusive.",
157 line);
158 return -1;
159 } else if (df->r_ret && df->ret) {
160 sp_log_err("config",
161 "Invalid configuration line: 'sp.disabled_functions%s':"
162 "'.r_ret' and '.ret' are mutually exclusive.",
163 line);
164 return -1;
165 } else if ((df->r_ret || df->ret) && (df->r_param || df->param)) {
166 sp_log_err("config",
167 "Invalid configuration line: 'sp.disabled_functions%s':"
168 "`ret` and `param` are mutually exclusives.",
169 line);
170 return -1;
171 } else if (!(df->r_function || df->function)) {
172 sp_log_err("config",
173 "Invalid configuration line: 'sp.disabled_functions%s':"
174 " must take a function name.",
175 line);
176 return -1;
177 } else if (!(df->allow ^ df->drop)) {
178 sp_log_err("config",
179 "Invalid configuration line: 'sp.disabled_functions%s': The "
180 "rule must either be a `drop` or and `allow` one.",
181 line);
182 return -1;
183 }
184
185 if (df->param && strchr(df->param, '[')) { // assume that this is an array
186 df->param_array_keys = sp_new_list();
187 if (0 != array_to_list(&df->param, &df->param_array_keys)) {
188 pefree(df->param_array_keys, 1);
189 return -1;
190 }
191 df->param_is_array = 1;
192 }
193
194 if (df->var && strchr(df->var, '[')) { // assume that this is an array
195 df->var_array_keys = sp_new_list();
196 if (0 != array_to_list(&df->var, &df->var_array_keys)) {
197 pefree(df->var_array_keys, 1);
198 return -1;
199 }
200 df->var_is_array = 1;
201 }
202
203 bool match = false;
204 const char *key[4] = {"include", "include_once", "require", "require_once"};
205 for (size_t i = 0; i < 4; i++) {
206 if (df->r_function && true == is_regexp_matching(df->r_function, key[i])) {
207 match = true;
208 break;
209 } else if (df->function && 0 == strcmp(df->function, key[i])) {
210 match = true;
211 break;
212 }
213 }
214 if (true == match && df->regexp) {
215 sp_list_insert(
216 SNUFFLEUPAGUS_G(config).config_regexp_inclusion->regexp_inclusion,
217 df->regexp);
218 } else if (df->ret || df->r_ret || df->ret_type) {
219 sp_list_insert(
220 SNUFFLEUPAGUS_G(config).config_disabled_functions_ret->disabled_functions,
221 df);
222 } else {
223 sp_list_insert(
224 SNUFFLEUPAGUS_G(config).config_disabled_functions->disabled_functions,
225 df);
226 }
227 return ret;
228}
229
230int parse_upload_validation(char *line) {
231 bool disable = false, enable = false;
232 sp_config_functions sp_config_funcs_upload_validation[] = {
233 {parse_str, SP_TOKEN_UPLOAD_SCRIPT,
234 &(SNUFFLEUPAGUS_G(config).config_upload_validation->script)},
235 {parse_empty, SP_TOKEN_SIMULATION,
236 &(SNUFFLEUPAGUS_G(config).config_upload_validation->simulation)},
237 {parse_empty, SP_TOKEN_ENABLE, &(enable)},
238 {parse_empty, SP_TOKEN_DISABLE, &(disable)},
239 {0}};
240
241 int ret = parse_keywords(sp_config_funcs_upload_validation, line);
242
243 if (0 != ret) {
244 return ret;
245 }
246
247 if (!(enable ^ disable)) {
248 sp_log_err("config", "A rule can't be enabled and disabled.");
249 return -1;
250 }
251 SNUFFLEUPAGUS_G(config).config_upload_validation->enable = enable;
252
253 char const *script = SNUFFLEUPAGUS_G(config).config_upload_validation->script;
254
255 if (!script) {
256 sp_log_err("config", "The `script` directive is mandatory in %s",
257 line);
258 return -1;
259 } else if (-1 == access(script, F_OK)) {
260 sp_log_err("config", "The `script` (%s) doesn't exist.", script);
261 return -1;
262 } else if (-1 == access(script, X_OK)) {
263 sp_log_err("config", "The `script` (%s) isn't executable.", script);
264 return -1;
265 }
266
267 return ret;
268}
diff --git a/src/sp_config_keywords.h b/src/sp_config_keywords.h
new file mode 100644
index 0000000..40fac47
--- /dev/null
+++ b/src/sp_config_keywords.h
@@ -0,0 +1,16 @@
1#ifndef SP_CONFIG_KEYWORDS_H
2#define SP_CONFIG_KEYWORDS_H
3#include "php_snuffleupagus.h"
4
5int parse_random(char *line);
6int parse_disable_xxe(char *line);
7int parse_auto_cookie_secure(char *line);
8int parse_global_strict(char *line);
9int parse_global(char *line) ;
10int parse_cookie_encryption(char *line);
11int parse_unserialize(char *line) ;
12int parse_readonly_exec(char *line);
13int parse_disabled_functions(char *line) ;
14int parse_upload_validation(char *line);
15
16#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 0000000..e05e95e
--- /dev/null
+++ b/src/sp_config_utils.c
@@ -0,0 +1,211 @@
1#include "php_snuffleupagus.h"
2
3static int validate_int(const char *value);
4static int validate_str(const char *value);
5
6static sp_pure int validate_int(const char *value) {
7 for (size_t i = 0; i < strlen(value); i++) {
8 if (!isdigit(value[i])) {
9 return -1;
10 }
11 }
12 return 0;
13}
14
15static sp_pure int validate_str(const char *value) {
16 int balance = 0; // ghetto [] validation
17
18 if (!strchr(value, '[')) {
19 return 0;
20 }
21
22 for (size_t i = 0; i < strlen(value); i++) {
23 if (value[i] == '[') {
24 balance++;
25 } else if (value[i] == ']') {
26 balance--;
27 }
28 if (balance < 0) {
29 return -1;
30 }
31 }
32 return balance != 0;
33}
34
35int parse_keywords(sp_config_functions *funcs, char *line) {
36 int value_len = 0;
37 const char *original_line = line;
38 for (size_t i = 0; funcs[i].func; i++) {
39 if (!strncmp(funcs[i].token, line, strlen(funcs[i].token))) {
40 line += strlen(funcs[i].token);
41 value_len = funcs[i].func(line, funcs[i].token, funcs[i].retval) + 1;
42 if (value_len == 0) { // bad parameter
43 return -1;
44 }
45 line += value_len;
46 i = -1; // we start the loop again
47 }
48 }
49 while (*line == ';' || *line == '\t' || *line == ' ') {
50 line++;
51 }
52
53 if (*line == '#') {
54 return 0;
55 }
56
57 if (*line) {
58 sp_log_err("config", "Trailing chars '%s' at the end of '%s'.", line,
59 original_line);
60 return -1;
61 }
62 return 0;
63}
64
65static char *get_string(size_t *consumed, char *restrict line,
66 const char *restrict keyword) {
67 enum { IN_ESCAPE, NONE } state = NONE;
68 char *original_line = line;
69 size_t j = 0;
70
71 char *ret = NULL;
72 if (NULL == line) {
73 goto err;
74 }
75
76 ret = pecalloc(sizeof(char), strlen(original_line) + 1, 1);
77
78 /* The first char of a string is always '"', since they MUST be quoted. */
79 if ('"' == *line) {
80 line++;
81 } else {
82 goto err;
83 }
84
85 for (size_t i = 0; line[i] && j < strlen(original_line) - 2; i++) {
86 switch (line[i]) {
87 case '"':
88 /* A double quote at this point is either:
89 - at the very end of the string.
90 - escaped
91 */
92 if ((state == NONE) && (line[i + 1] == SP_TOKEN_END_PARAM)) {
93 /* The `+2` if for
94 1. the terminal double-quote
95 2. the SP_TOKEN_END_PARAM
96 */
97 *consumed = i + 2;
98 return ret;
99 } else if (state == IN_ESCAPE) {
100 break; // we're on an escped double quote
101 } else {
102 goto err;
103 }
104 case '\\':
105 if (state == NONE) {
106 state = IN_ESCAPE;
107 continue;
108 }
109 default:
110 break;
111 }
112 if (state == IN_ESCAPE) {
113 state = NONE;
114 }
115 ret[j++] = line[i];
116 }
117err:
118 sp_log_err("error",
119 "There is an issue with the parsing of '%s': it doesn't look like a valid string.",
120 original_line ? original_line : "NULL");
121 line = NULL;
122 return NULL;
123}
124
125static char *get_misc(char *restrict line, const char *restrict keyword) {
126 size_t i = 0;
127 char *ret = pecalloc(sizeof(char), 1024, 1);
128
129 while (i < 1024 - 1 && line[i] && line[i] != SP_TOKEN_END_PARAM) {
130 ret[i] = line[i];
131 i++;
132 }
133
134 if (line[i] != SP_TOKEN_END_PARAM) {
135 if (i >= 1024 - 1) {
136 sp_log_err("config", "The following line is too long: %s.", line);
137 } else {
138 sp_log_err("config", "Missing closing %c in line %s.", SP_TOKEN_END_PARAM,
139 line);
140 }
141 return NULL;
142 } else if (i == 0) {
143 sp_log_err("config", "The keyword %s%c is expecting a parameter.",
144 keyword, SP_TOKEN_END_PARAM);
145 return NULL;
146 }
147 return ret;
148}
149
150char *get_param(size_t *consumed, char *restrict line, sp_type type,
151 const char *restrict keyword) {
152 char *retval = NULL;
153 if (type == SP_TYPE_STR) {
154 retval = get_string(consumed, line, keyword);
155 } else {
156 retval = get_misc(line, keyword);
157 *consumed = retval ? strlen(retval) : 0;
158 }
159
160 if (retval) {
161 if (type == SP_TYPE_STR && 0 == validate_str(retval)) {
162 return retval;
163 } else if (type == SP_TYPE_INT && 0 == validate_int(retval)) {
164 return retval;
165 }
166 }
167 return NULL;
168}
169
170// FIXME this is leaking like hell @blotus
171int array_to_list(char **name_ptr, sp_node_t **keys) {
172 int in_key = 0;
173 size_t i = 0;
174 char *name = *name_ptr;
175 char *key_name = ecalloc(strlen(name) + 1, 1); // im way too lazy for
176 // now
177 char *tmp = ecalloc(strlen(name) + 1, 1);
178
179 for (i = 0; name[i] != '['; i++) {
180 tmp[i] = name[i];
181 }
182 tmp[i] = 0;
183
184 for (size_t j = 0; name[i]; i++) {
185 const char c = name[i];
186 if (c == '[') {
187 if (in_key == 0) {
188 in_key = 1;
189 } else {
190 efree(key_name);
191 return -1;
192 }
193 } else if (c == ']') {
194 if (in_key == 0) {
195 efree(key_name);
196 return -1;
197 } else {
198 in_key = 0;
199 j = 0;
200 sp_list_insert(*keys, pestrdup(key_name, 1));
201 memset(key_name, 0, strlen(name) + 1);
202 }
203 } else if (in_key == 1) {
204 key_name[j] = c;
205 j++;
206 }
207 }
208 efree(key_name);
209 *name_ptr = pestrdup(tmp, 1);
210 return in_key;
211}
diff --git a/src/sp_config_utils.h b/src/sp_config_utils.h
new file mode 100644
index 0000000..f2f8fce
--- /dev/null
+++ b/src/sp_config_utils.h
@@ -0,0 +1,8 @@
1#ifndef SP_CONFIG_UTILS
2#define SP_CONFIG_UTILS
3
4int parse_keywords(sp_config_functions *, char *);
5char *get_param(size_t *, char *restrict, sp_type, const char *restrict);
6int array_to_list(char **, sp_node_t **);
7
8#endif /* SP_CONFIG_UTILS */
diff --git a/src/sp_cookie_encryption.c b/src/sp_cookie_encryption.c
new file mode 100644
index 0000000..5248486
--- /dev/null
+++ b/src/sp_cookie_encryption.c
@@ -0,0 +1,216 @@
1#include "php_snuffleupagus.h"
2
3#include "ext/standard/url.h"
4
5ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
6
7static unsigned int nonce_d = 0;
8
9static inline void generate_key(unsigned char *key) {
10 PHP_SHA256_CTX ctx;
11 const char *user_agent = sp_getenv("HTTP_USER_AGENT");
12 const char *remote_addr = sp_getenv("REMOTE_ADDR");
13 const char *encryption_key =
14 SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key;
15
16 /* 32 is the size of a SHA256. */
17 assert(32 == crypto_secretbox_KEYBYTES);
18
19 PHP_SHA256Init(&ctx);
20
21 if (user_agent) {
22 PHP_SHA256Update(&ctx, (unsigned char *)user_agent, strlen(user_agent));
23 }
24
25 if (remote_addr) {
26 char out[128];
27 apply_mask_on_ip(out, remote_addr);
28 PHP_SHA256Update(&ctx, (unsigned char*)out, sizeof(out));
29 }
30
31 if (encryption_key) {
32 PHP_SHA256Update(&ctx, (const unsigned char *)encryption_key,
33 strlen(encryption_key));
34 }
35
36 PHP_SHA256Final((unsigned char *)key, &ctx);
37}
38
39int decrypt_cookie(zval *pDest, int num_args, va_list args,
40 zend_hash_key *hash_key) {
41 unsigned char key[crypto_secretbox_KEYBYTES] = {0};
42 size_t value_len;
43 zend_string *debase64;
44 unsigned char *decrypted;
45 int ret = 0;
46
47 /* If the cookie isn't in the conf, it shouldn't be encrypted. */
48 if (0 ==
49 zend_hash_exists(SNUFFLEUPAGUS_G(config).config_cookie_encryption->names,
50 hash_key->key)) {
51 return ZEND_HASH_APPLY_KEEP;
52 }
53
54 generate_key(key);
55
56 value_len = php_url_decode(Z_STRVAL_P(pDest), Z_STRLEN_P(pDest));
57
58 if (value_len == 0) {
59 return ZEND_HASH_APPLY_KEEP;
60 }
61
62 debase64 = php_base64_decode((unsigned char *)(Z_STRVAL_P(pDest)), value_len);
63
64 if (value_len <
65 crypto_secretbox_NONCEBYTES + crypto_secretbox_ZEROBYTES) {
66 sp_log_msg("cookie_encryption", LOG_DROP,
67 "Buffer underflow tentative detected in cookie encryption handling.");
68 return ZEND_HASH_APPLY_REMOVE;
69 }
70
71 decrypted = pecalloc(value_len, 1, 0);
72
73 ret = crypto_secretbox_open(
74 decrypted,
75 (unsigned char *)(ZSTR_VAL(debase64) + crypto_secretbox_NONCEBYTES),
76 ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES,
77 (unsigned char *)ZSTR_VAL(debase64), key);
78
79 if (ret == -1) {
80 sp_log_msg("cookie_encryption", LOG_DROP,
81 "Something went wrong with the decryption of %s.",
82 ZSTR_VAL(hash_key->key));
83 return ZEND_HASH_APPLY_REMOVE;
84 }
85
86 ZVAL_STRINGL(pDest, (char *)(decrypted + crypto_secretbox_ZEROBYTES),
87 ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES - 1 -
88 crypto_secretbox_ZEROBYTES);
89
90 return ZEND_HASH_APPLY_KEEP;
91}
92
93/**
94 This function will return the `data` of length `data_len` encrypted in the
95 form
96 base64(nonce | encrypted_data) (with `|` being the concatenation
97 operation).
98
99 The `nonce` is time-based.
100*/
101static zend_string *encrypt_data(char *data, unsigned long long data_len) {
102 const size_t encrypted_msg_len = crypto_secretbox_ZEROBYTES + data_len + 1;
103 const size_t emsg_and_nonce_len = encrypted_msg_len + crypto_secretbox_NONCEBYTES;
104
105 unsigned char key[crypto_secretbox_KEYBYTES] = {0};
106 unsigned char nonce[crypto_secretbox_NONCEBYTES] = {0};
107 unsigned char *data_to_encrypt = pecalloc(encrypted_msg_len, 1, 0);
108 unsigned char *encrypted_data = pecalloc(emsg_and_nonce_len, 1, 1);
109
110 generate_key(key);
111
112 /* tweetnacl's API requires the message to be padded with
113 crypto_secretbox_ZEROBYTES zeroes. */
114 memcpy(data_to_encrypt + crypto_secretbox_ZEROBYTES, data, data_len);
115
116 assert(sizeof(size_t) <= crypto_secretbox_NONCEBYTES);
117
118 nonce_d++;
119 sscanf((char*)nonce, "%ud", &nonce_d);
120
121 memcpy(encrypted_data, nonce, crypto_secretbox_NONCEBYTES);
122 crypto_secretbox(encrypted_data + crypto_secretbox_NONCEBYTES,
123 data_to_encrypt, encrypted_msg_len, nonce, key);
124
125 zend_string *z = php_base64_encode(encrypted_data, emsg_and_nonce_len);
126 sp_log_debug("cookie_encryption", "Cookie value:%s:", z->val);
127 return z;
128}
129
130PHP_FUNCTION(sp_setcookie) {
131 zval params[7] = { 0 };
132 zend_string *name = NULL, *value = NULL, *path = NULL, *domain = NULL;
133 zend_long expires = 0;
134 zend_bool secure = 0, httponly = 0;
135 zval ret_val;
136 zval func_name;
137
138 ZEND_PARSE_PARAMETERS_START(1, 7)
139 Z_PARAM_STR(name)
140 Z_PARAM_OPTIONAL
141 Z_PARAM_STR(value)
142 Z_PARAM_LONG(expires)
143 Z_PARAM_STR(path)
144 Z_PARAM_STR(domain)
145 Z_PARAM_BOOL(secure)
146 Z_PARAM_BOOL(httponly)
147 ZEND_PARSE_PARAMETERS_END();
148
149 /* If the request was issued over HTTPS, the cookie should be "secure" */
150 if (SNUFFLEUPAGUS_G(config).config_auto_cookie_secure) {
151 const zval server_vars = PG(http_globals)[TRACK_VARS_SERVER];
152 if (Z_TYPE(server_vars) == IS_ARRAY) {
153 const zval *is_https =
154 zend_hash_str_find(Z_ARRVAL(server_vars), "HTTPS", strlen("HTTPS"));
155 if (NULL != is_https) {
156 secure = 1;
157 }
158 }
159 }
160
161 /* If the cookie's value is encrypted, it won't be usable by
162 * javascript anyway.
163 */
164 if (zend_hash_exists(SNUFFLEUPAGUS_G(config).config_cookie_encryption->names,
165 name) > 0) {
166 httponly = 1;
167 }
168
169 /* Shall we encrypt the cookie's value? */
170 if (zend_hash_exists(SNUFFLEUPAGUS_G(config).config_cookie_encryption->names,
171 name) > 0 && value) {
172 zend_string *encrypted_data = encrypt_data(value->val, value->len);
173 ZVAL_STR_COPY(&params[1], encrypted_data);
174 zend_string_release(encrypted_data);
175 } else if (value) {
176 ZVAL_STR_COPY(&params[1], value);
177 }
178
179 ZVAL_STRING(&func_name, "setcookie");
180 ZVAL_STR_COPY(&params[0], name);
181 ZVAL_LONG(&params[2], expires);
182 if (path) {
183 ZVAL_STR_COPY(&params[3], path);
184 }
185 if (domain) {
186 ZVAL_STR_COPY(&params[4], domain);
187 }
188 if (secure) {
189 ZVAL_LONG(&params[5], secure);
190 }
191 if (httponly) {
192 ZVAL_LONG(&params[6], httponly);
193 }
194
195 /* This is the _fun_ part: because PHP is utterly idiotic and nonsensical,
196 the `call_user_function` macro will __discard__ (yes) its first argument
197 (the hashtable), effectively calling functions from `CG(function_table)`.
198 This is why were replacing our hook with the original function, calling
199 the function, and then re-hooking it. */
200 void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
201 handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook), "setcookie",
202 strlen("setcookie"));
203 zend_internal_function *func = zend_hash_str_find_ptr(
204 CG(function_table), "setcookie", strlen("setcookie"));
205 func->handler = handler;
206
207 call_user_function(CG(function_table), NULL, &func_name, &ret_val, 7, params);
208
209 func->handler = PHP_FN(sp_setcookie);
210}
211
212int hook_cookies() {
213 HOOK_FUNCTION("setcookie", sp_internal_functions_hook, PHP_FN(sp_setcookie), false);
214
215 return SUCCESS;
216}
diff --git a/src/sp_cookie_encryption.h b/src/sp_cookie_encryption.h
new file mode 100644
index 0000000..9904738
--- /dev/null
+++ b/src/sp_cookie_encryption.h
@@ -0,0 +1,17 @@
1
2#ifndef __SP_COOKIE_ENCRYPTION
3#define __SP_COOKIE_ENCRYPTION
4
5#include "SAPI.h"
6#include "tweetnacl.h"
7
8#include "sp_utils.h"
9
10#include "ext/hash/php_hash.h"
11#include "ext/hash/php_hash_sha.h"
12#include "ext/standard/base64.h"
13
14int hook_cookies();
15int decrypt_cookie(zval *pDest, int num_args, va_list args, zend_hash_key *hash_key);
16
17#endif /* __SP_COOKIE_ENCRYPTION */
diff --git a/src/sp_disable_xxe.c b/src/sp_disable_xxe.c
new file mode 100644
index 0000000..d11b3d0
--- /dev/null
+++ b/src/sp_disable_xxe.c
@@ -0,0 +1,25 @@
1#include "php_snuffleupagus.h"
2
3ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
4
5PHP_FUNCTION(sp_libxml_disable_entity_loader) { RETURN_TRUE; }
6
7int hook_libxml_disable_entity_loader() {
8 zval func_name;
9 zval hmac;
10 zval params[1];
11
12 TSRMLS_FETCH();
13
14 /* Call the php function here instead of re-implementing it is a bit
15 * ugly, but we do not want to introduce compile-time dependencies against
16 * libxml. */
17 ZVAL_STRING(&func_name, "libxml_disable_entity_loader");
18 ZVAL_STRING(&params[0], "true");
19 call_user_function(CG(function_table), NULL, &func_name, &hmac, 1, params);
20
21 HOOK_FUNCTION("libxml_disable_entity_loader", sp_internal_functions_hook,
22 PHP_FN(sp_libxml_disable_entity_loader), false);
23
24 return SUCCESS;
25}
diff --git a/src/sp_disable_xxe.h b/src/sp_disable_xxe.h
new file mode 100644
index 0000000..274c169
--- /dev/null
+++ b/src/sp_disable_xxe.h
@@ -0,0 +1,6 @@
1#ifndef __SP_DISABLE_XXE_H
2#define __SP_DISABLE_XXE_H
3
4int hook_libxml_disable_entity_loader();
5
6#endif /* __SP_DISABLE_XXE_H */
diff --git a/src/sp_disabled_functions.c b/src/sp_disabled_functions.c
new file mode 100644
index 0000000..55d782b
--- /dev/null
+++ b/src/sp_disabled_functions.c
@@ -0,0 +1,356 @@
1#include "php_snuffleupagus.h"
2
3#include "zend_execute.h"
4#include "zend_hash.h"
5
6ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus);
7
8ZEND_COLD static zend_always_inline bool is_hash_matching(
9 const char* current_filename,
10 sp_disabled_function const* const config_node) {
11 char current_file_hash[SHA256_SIZE * 2];
12 compute_hash(current_filename, current_file_hash);
13 return (0 == strncmp(current_file_hash, config_node->hash, SHA256_SIZE));
14}
15
16static zend_always_inline char* get_complete_function_path(
17 zend_execute_data const* const execute_data) {
18 char const* class_name;
19 char const* const function_name =
20 ZSTR_VAL(execute_data->func->common.function_name);
21 char* complete_path_function = NULL;
22
23 class_name = get_active_class_name(NULL);
24 if (*class_name) {
25 const size_t len = strlen(class_name) + 2 + strlen(function_name) + 1;
26 complete_path_function = emalloc(len);
27 snprintf(complete_path_function, len, "%s::%s", class_name, function_name);
28 } else {
29 complete_path_function = estrdup(function_name);
30 }
31 return complete_path_function;
32}
33
34static bool is_local_var_matching(zend_execute_data *execute_data, const sp_disabled_function *const config_node) {
35 zend_execute_data *orig_execute_data = execute_data;
36
37 /*because execute_data points to hooked function data,
38 which we dont care about */
39 zend_execute_data *current = execute_data->prev_execute_data;
40 zval *value = NULL;
41
42 while (current) {
43 zend_string *key = NULL;
44 EG(current_execute_data) = current;
45 zend_array *symtable = zend_rebuild_symbol_table();
46 ZEND_HASH_FOREACH_STR_KEY_VAL(symtable, key, value) {
47 if (0 == strcmp(config_node->var, key->val)) { // is the var name right?
48 if (Z_TYPE_P(value) == IS_INDIRECT) {
49 value = Z_INDIRECT_P(value);
50 }
51 if (Z_TYPE_P(value) != IS_ARRAY) {
52 char *var_value_str = sp_convert_to_string(value);
53 if (true == sp_match_value(var_value_str, config_node->value, config_node->regexp)) {
54 efree(var_value_str);
55 EG(current_execute_data) = orig_execute_data;
56 return true;
57 }
58 efree(var_value_str);
59 }
60 else {
61 EG(current_execute_data) = orig_execute_data;
62 return sp_match_array_key_recurse(value, config_node->var_array_keys, config_node->value, NULL);
63 }
64 }
65 }
66 ZEND_HASH_FOREACH_END();
67 current = current->prev_execute_data;
68 }
69
70 EG(current_execute_data) = orig_execute_data;
71 return false;
72}
73
74bool should_disable(zend_execute_data* execute_data) {
75 const char* current_filename = zend_get_executed_filename(TSRMLS_C);
76 const sp_node_t* config =
77 SNUFFLEUPAGUS_G(config).config_disabled_functions->disabled_functions;
78 const char* function_name =
79 ZSTR_VAL(execute_data->func->common.function_name);
80 char* complete_path_function;
81 char const* client_ip = sp_getenv("REMOTE_ADDR");
82
83 if (!function_name) {
84 return false;
85 }
86
87 if (!config || !config->data) {
88 return false;
89 }
90
91 complete_path_function = get_complete_function_path(execute_data);
92
93 while (config) {
94 sp_disabled_function const* const config_node =
95 (sp_disabled_function*)(config->data);
96 const char* arg_name = NULL;
97 const char* arg_value_str = NULL;
98
99 if (false == config_node->enable) {
100 goto next;
101 }
102
103 if (config_node->function) { /* Litteral match against the function name. */
104 if (0 != strcmp(config_node->function, complete_path_function)) {
105 goto next;
106 }
107 } else if (config_node->r_function) {
108 if (false ==
109 is_regexp_matching(config_node->r_function, complete_path_function)) {
110 goto next;
111 }
112 }
113 if (config_node->var) {
114 if (false == is_local_var_matching(execute_data, config_node)) {
115 goto next;
116 }
117 }
118
119 if (config_node->filename) { /* Check the current file name. */
120 if (0 != strcmp(current_filename, config_node->filename)) {
121 goto next;
122 }
123 } else if (config_node->r_filename) {
124 if (false ==
125 is_regexp_matching(config_node->r_filename, current_filename)) {
126 goto next;
127 }
128 }
129
130 if (config_node->hash) {
131 if (false == is_hash_matching(current_filename, config_node)) {
132 goto next;
133 }
134 }
135
136 if (client_ip && config_node->cidr &&
137 (false == cidr_match(client_ip, config_node->cidr))) {
138 goto next;
139 }
140
141 /* Check if we filter on parameter value*/
142 if (config_node->param || config_node->r_param) {
143 const unsigned int nb_param = execute_data->func->common.num_args;
144 bool arg_matched = false;
145
146 for (unsigned int i = 0; i < nb_param; i++) {
147 arg_matched = false;
148 if (ZEND_USER_CODE(execute_data->func->type)) { // yay consistency
149 arg_name = ZSTR_VAL(execute_data->func->common.arg_info[i].name);
150 } else {
151 arg_name = execute_data->func->internal_function.arg_info[i].name;
152 }
153
154 const bool arg_matching =
155 config_node->param && (0 == strcmp(arg_name, config_node->param));
156 const bool pcre_matching =
157 config_node->r_param &&
158 (true == is_regexp_matching(config_node->r_param, arg_name));
159
160 /* This is the parameter name we're looking for. */
161 if (true == arg_matching || true == pcre_matching) {
162 zval* arg_value = ZEND_CALL_VAR_NUM(execute_data, i);
163
164 if (config_node->param_type) { // Are we matching on the `type`?
165 if (config_node->param_type == Z_TYPE_P(arg_value)) {
166 arg_matched = true;
167 break;
168 }
169 } else if (Z_TYPE_P(arg_value) == IS_ARRAY) {
170 arg_value_str = estrdup("Array");
171 // match on arr -> match on all key content, if a key is an array,
172 // ignore it
173 // match on arr[foo] -> match only on key foo, if the key is an
174 // array, match on all keys content
175 if (config_node->param_is_array == true) {
176 if (true == sp_match_array_key_recurse(
177 arg_value, config_node->param_array_keys,
178 config_node->value, config_node->regexp)) {
179 arg_matched = true;
180 break;
181 }
182 } else { // match on all keys, but don't go into subarray
183 if (true == sp_match_array_key(arg_value, config_node->value,
184 config_node->regexp)) {
185 arg_matched = true;
186 break;
187 }
188 }
189 } else {
190 arg_value_str = sp_convert_to_string(arg_value);
191 if (true == sp_match_value(arg_value_str, config_node->value,
192 config_node->regexp)) {
193 arg_matched = true;
194 break;
195 }
196 }
197 }
198 }
199 if (false == arg_matched) {
200 goto next;
201 }
202 }
203
204 /* Everything matched.*/
205
206 if (true == config_node->allow) {
207 goto allow;
208 }
209
210 sp_log_disable(complete_path_function, arg_name, arg_value_str,
211 config_node);
212 if (true == config_node->simulation) {
213 goto next;
214 } else { // We've got a match, the function won't be executed
215 efree(complete_path_function);
216 return true;
217 }
218next:
219config = config->next;
220 }
221allow:
222 efree(complete_path_function);
223 return false;
224}
225
226static bool should_drop_on_ret(zval* return_value,
227 const zend_execute_data* const execute_data) {
228 const sp_node_t* config =
229 SNUFFLEUPAGUS_G(config).config_disabled_functions_ret->disabled_functions;
230 char* complete_path_function = get_complete_function_path(execute_data);
231 const char* current_filename = zend_get_executed_filename(TSRMLS_C);
232
233 if (!config || !config->data) {
234 return false;
235 }
236
237 while (config) {
238 char* ret_value_str = NULL;
239 sp_disabled_function const* const config_node =
240 (sp_disabled_function*)(config->data);
241
242 if (false == config_node->enable) {
243 goto next;
244 }
245
246 if (config_node->function) {
247 if (0 != strcmp(config_node->function, complete_path_function)) {
248 goto next;
249 }
250 } else if (config_node->r_function) {
251 if (false ==
252 is_regexp_matching(config_node->r_function, complete_path_function)) {
253 goto next;
254 }
255 }
256
257 if (config_node->filename) { /* Check the current file name. */
258 if (0 != strcmp(current_filename, config_node->filename)) {
259 goto next;
260 }
261 } else if (config_node->r_filename) {
262 if (false ==
263 is_regexp_matching(config_node->r_filename, current_filename)) {
264 goto next;
265 }
266 }
267
268 if (config_node->hash) {
269 if (false == is_hash_matching(current_filename, config_node)) {
270 goto next;
271 }
272 }
273
274 ret_value_str = sp_convert_to_string(return_value); // FIXME memleak
275
276 bool match_type = (config_node->ret_type) &&
277 (config_node->ret_type == Z_TYPE_P(return_value));
278 bool match_value = (config_node->ret || config_node->r_ret) &&
279 (true == sp_match_value(ret_value_str, config_node->ret,
280 config_node->r_ret));
281
282 if (true == match_type || match_value) {
283 if (true == config_node->allow) {
284 efree(complete_path_function);
285 return false;
286 }
287 sp_log_disable_ret(complete_path_function, ret_value_str, config_node);
288 if (false == config_node->simulation) {
289 efree(complete_path_function);
290 return true;
291 }
292 }
293 next:
294 config = config->next;
295 }
296 efree(complete_path_function);
297 return false;
298}
299
300ZEND_FUNCTION(check_disabled_function) {
301 void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS);
302 const char* current_function_name = get_active_function_name(TSRMLS_C);
303
304 if (true == should_disable(execute_data)) {
305 return;
306 }
307
308 if ((orig_handler = zend_hash_str_find_ptr(
309 SNUFFLEUPAGUS_G(disabled_functions_hook), current_function_name,
310 strlen(current_function_name)))) {
311 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
312 if (true == should_drop_on_ret(return_value, execute_data)) {
313 zend_bailout();
314 }
315 } else {
316 sp_log_err(
317 "disabled_functions",
318 "Unable to find the pointer to the original function '%s' in the "
319 "hashtable.\n",
320 current_function_name);
321 }
322}
323
324static int hook_functions(const sp_node_t* config) {
325 while (config && config->data) {
326 const char* function_name = ((sp_disabled_function*)config->data)->function;
327 const pcre* function_name_regexp =
328 ((sp_disabled_function*)config->data)->r_function;
329
330 if (NULL != function_name) { // hook function by name
331 HOOK_FUNCTION(function_name, disabled_functions_hook,
332 PHP_FN(check_disabled_function), false);
333 } else if (NULL != function_name_regexp) { // hook function by regexp
334 HOOK_FUNCTION_BY_REGEXP(function_name_regexp, disabled_functions_hook,
335 PHP_FN(check_disabled_function), false);
336 } else {
337 return FAILURE;
338 }
339
340 config = config->next;
341 }
342 return SUCCESS;
343}
344
345int hook_disabled_functions(void) {
346 TSRMLS_FETCH();
347
348 int ret = SUCCESS;
349
350 ret |= hook_functions(
351 SNUFFLEUPAGUS_G(config).config_disabled_functions->disabled_functions);
352 ret |= hook_functions(SNUFFLEUPAGUS_G(config)
353 .config_disabled_functions_ret->disabled_functions);
354
355 return ret;
356}
diff --git a/src/sp_disabled_functions.h b/src/sp_disabled_functions.h
new file mode 100644
index 0000000..680eabb
--- /dev/null
+++ b/src/sp_disabled_functions.h
@@ -0,0 +1,11 @@
1#ifndef __SP_DISABLE_FUNCTIONS_H
2#define __SP_DISABLE_FUNCTIONS_H
3
4#include "ext/hash/php_hash.h"
5#include "ext/hash/php_hash_sha.h"
6#include "ext/standard/md5.h"
7
8int hook_disabled_functions();
9bool should_disable(zend_execute_data* function_name);
10
11#endif /* __SP_DISABLE_FUNCTIONS_H */
diff --git a/src/sp_execute.c b/src/sp_execute.c
new file mode 100644
index 0000000..faf126c
--- /dev/null
+++ b/src/sp_execute.c
@@ -0,0 +1,100 @@
1#include "php_snuffleupagus.h"
2
3#include <errno.h>
4#include <string.h>
5
6ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus);
7
8static void (*orig_execute_ex)(zend_execute_data *execute_data);
9static int (*orig_zend_stream_open)(const char *filename,
10 zend_file_handle *handle);
11
12// FIXME handle symlink
13ZEND_COLD static inline void terminate_if_writable(const char *filename) {
14 if (0 == access(filename, W_OK)) {
15 if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->simulation) {
16 sp_log_msg("readonly_exec", LOG_NOTICE,
17 "Attempted execution of a writable file (%s).", filename);
18 } else {
19 sp_log_msg("readonly_exec", LOG_DROP,
20 "Attempted execution of a writable file (%s).", filename);
21 sp_terminate();
22 }
23 } else {
24 if (EACCES != errno) {
25 sp_log_err("Writable execution", "Error while accessing %s: %s", filename,
26 strerror(errno));
27 }
28 }
29}
30
31static void check_inclusion_regexp(const char * const filename) {
32 if (SNUFFLEUPAGUS_G(config).config_regexp_inclusion->regexp_inclusion) {
33 const sp_node_t* config = SNUFFLEUPAGUS_G(config).config_regexp_inclusion->regexp_inclusion;
34 if (!config || !config->data) {
35 return;
36 }
37 while (config) {
38 pcre *config_node = (pcre*)(config->data);
39 if (false == is_regexp_matching(config_node, filename)) {
40 sp_log_msg("include", LOG_DROP, "Inclusion of a forbidden file (%s).", filename);
41 sp_terminate();
42 }
43 config = config->next;
44 }
45 }
46}
47
48static void sp_execute_ex(zend_execute_data *execute_data) {
49 if (NULL == execute_data->func->common.function_name) {
50 goto execute;
51 }
52
53 if (true == should_disable(execute_data)) {
54 return;
55 }
56
57 if (execute_data->func->op_array.type == ZEND_EVAL_CODE) {
58 sp_log_debug("Currently in an eval\n");
59 }
60
61 if (NULL != execute_data->func->op_array.filename) {
62 if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) {
63 terminate_if_writable(ZSTR_VAL(execute_data->func->op_array.filename));
64 }
65}
66
67execute:
68 orig_execute_ex(execute_data);
69}
70
71static int sp_stream_open(const char *filename,
72 zend_file_handle *handle) {
73 const zend_execute_data *data = EG(current_execute_data);
74
75 if ((NULL != data) && (NULL != data->opline) &&
76 (ZEND_INCLUDE_OR_EVAL == data->opline->opcode)) {
77 if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) {
78 terminate_if_writable(filename);
79 }
80 check_inclusion_regexp(filename);
81 }
82 return orig_zend_stream_open(filename, handle);
83}
84
85int hook_execute(void) {
86 TSRMLS_FETCH();
87
88 /* zend_execute_ex is used for "classic" function calls */
89 orig_execute_ex = zend_execute_ex;
90 zend_execute_ex = sp_execute_ex;
91
92 /* zend_stream_open_function is used FIXME */
93 orig_zend_stream_open = zend_stream_open_function;
94 zend_stream_open_function = sp_stream_open;
95
96 /* zend_execute_internal is used for "indirect" functions call,
97 * like array_map or call_user_func. */
98
99 return SUCCESS;
100}
diff --git a/src/sp_execute.h b/src/sp_execute.h
new file mode 100644
index 0000000..8345736
--- /dev/null
+++ b/src/sp_execute.h
@@ -0,0 +1,6 @@
1#ifndef SP_EXECUTE_H
2#define SP_EXECUTE_H
3
4int hook_execute(void);
5
6#endif /* SP_EXECUTE_H */
diff --git a/src/sp_harden_rand.c b/src/sp_harden_rand.c
new file mode 100644
index 0000000..e0e35ff
--- /dev/null
+++ b/src/sp_harden_rand.c
@@ -0,0 +1,77 @@
1#include "php_snuffleupagus.h"
2
3extern ZEND_API zend_class_entry *zend_ce_error;
4
5ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
6
7/* This function is needed because `rand` and `mt_rand` parameters
8 * are optional, while the ones from `random_int` aren't. */
9static void random_int_wrapper(INTERNAL_FUNCTION_PARAMETERS) {
10 zend_long min, max, result;
11
12 switch (EX_NUM_ARGS()) {
13 case 0:
14 min = 0;
15 max = PHP_MT_RAND_MAX;
16 break;
17 case 1:
18 ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 1, 1);
19 Z_PARAM_LONG(min);
20 ZEND_PARSE_PARAMETERS_END();
21 max = PHP_MT_RAND_MAX;
22 break;
23 case 2:
24 default:
25 ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 0, 2);
26 Z_PARAM_LONG(min);
27 Z_PARAM_LONG(max);
28 ZEND_PARSE_PARAMETERS_END();
29 }
30
31 if (min > max) {
32 if (php_random_int_throw(max, min, &result) == FAILURE) {
33 return;
34 }
35 } else {
36 if (php_random_int_throw(min, max, &result) == FAILURE) {
37 return;
38 }
39 }
40
41 RETURN_LONG(result);
42}
43
44PHP_FUNCTION(sp_rand) {
45 void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS);
46
47 if ((orig_handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook), "rand",
48 strlen("rand")))) {
49 /* call the original `rand` function,
50 * since we might no be the only ones to hook it*/
51 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
52 }
53
54 random_int_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU);
55}
56
57PHP_FUNCTION(sp_mt_rand) {
58 void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS);
59
60 if ((orig_handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook),
61 "mt_rand", strlen("mt_rand")))) {
62 /* call the original `mt_rand` function,
63 * since we might no be the only ones to hook it*/
64 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
65 }
66
67 random_int_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU);
68}
69
70int hook_rand() {
71 TSRMLS_FETCH();
72
73 HOOK_FUNCTION("rand", sp_internal_functions_hook, PHP_FN(sp_rand), false);
74 HOOK_FUNCTION("mt_rand", sp_internal_functions_hook, PHP_FN(sp_mt_rand), false);
75
76 return SUCCESS;
77}
diff --git a/src/sp_harden_rand.h b/src/sp_harden_rand.h
new file mode 100644
index 0000000..53ebdd0
--- /dev/null
+++ b/src/sp_harden_rand.h
@@ -0,0 +1,10 @@
1#ifndef __SP_HARDEN_RAND_H
2#define __SP_HARDEN_RAND_H
3
4#include "ext/standard/php_rand.h"
5#include "ext/standard/php_random.h"
6#include "zend_exceptions.h"
7
8int hook_rand();
9
10#endif /* __SP_HARDEN_RAND_H */
diff --git a/src/sp_list.c b/src/sp_list.c
new file mode 100644
index 0000000..04154b7
--- /dev/null
+++ b/src/sp_list.c
@@ -0,0 +1,37 @@
1#include "sp_list.h"
2#include <stdio.h>
3#include <stdlib.h>
4#include "php_snuffleupagus.h"
5
6void sp_list_free(sp_node_t *node) {
7 while(node) {
8 sp_node_t *tmp = node->next;
9 pefree(node, 1);
10 node = tmp;
11 }
12}
13
14sp_node_t *sp_new_list() {
15 sp_node_t *new = pecalloc(sizeof(*new), 1, 1);
16 new->next = new->data = new->head = NULL;
17 return new;
18}
19
20void sp_list_insert(sp_node_t *list, void *data) {
21 if (list->head == NULL) {
22 list->data = data;
23 list->next = NULL;
24 list->head = list;
25 } else {
26 sp_node_t *new = pecalloc(sizeof(*new), 1, 1);
27
28 new->data = data;
29 new->next = NULL;
30 new->head = list;
31
32 while (list->next) {
33 list = list->next;
34 }
35 list->next = new;
36 }
37}
diff --git a/src/sp_list.h b/src/sp_list.h
new file mode 100644
index 0000000..48b11f6
--- /dev/null
+++ b/src/sp_list.h
@@ -0,0 +1,15 @@
1#ifndef SP_LIST_H
2#define SP_LIST_H
3
4typedef struct sp_node_s {
5 struct sp_node_s *next;
6 struct sp_node_s *head;
7 void *data;
8
9} sp_node_t;
10
11sp_node_t *sp_new_list();
12void sp_list_insert(sp_node_t *, void *);
13void sp_list_free(sp_node_t *);
14
15#endif
diff --git a/src/sp_network_utils.c b/src/sp_network_utils.c
new file mode 100644
index 0000000..28bc324
--- /dev/null
+++ b/src/sp_network_utils.c
@@ -0,0 +1,159 @@
1#include <arpa/inet.h>
2#include <netinet/in.h>
3#include <sys/socket.h>
4
5#include "php_snuffleupagus.h"
6
7ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
8
9static inline bool cidr4_match(const struct in_addr addr,
10 const struct in_addr net, uint8_t bits);
11static inline bool cidr6_match(const struct in6_addr address,
12 const struct in6_addr network, uint8_t bits);
13static inline int get_ip_version(const char *ip);
14
15void apply_mask_on_ip(char *out, const char *const remote_addr) {
16 uint8_t mask4 = SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv4;
17 uint8_t mask6 = SNUFFLEUPAGUS_G(config).config_cookie_encryption->mask_ipv6;
18 const int ip_version = get_ip_version(remote_addr);
19
20 memset(out, 0, 128);
21
22 if (ip_version == AF_INET) {
23 struct in_addr out4;
24 inet_pton(AF_INET, remote_addr, &out4);
25 const long n = out4.s_addr & htonl(0xFFFFFFFFu << (32 - mask4));
26 out[0] = (n >> 24) & 0xFF;
27 out[1] = (n >> 16) & 0xFF;
28 out[2] = (n >> 8) & 0xFF;
29 out[3] = (n >> 0) & 0xFF;
30 } else if (ip_version == AF_INET6) {
31 inet_pton(AF_INET6, remote_addr, out);
32 uint32_t *p_ip = (uint32_t *)out;
33 while (32 < mask6) {
34 *p_ip = 0xFFFFFFFFu;
35 p_ip++;
36 mask6 -= 32;
37 }
38 if (0 != mask6) {
39 *p_ip = htonl(0xFFFFFFFFu << (32 - mask6));
40 }
41 } else {
42 sp_log_err("ip_mask", "It seems that %s isn't a valid ip.", remote_addr);
43 }
44}
45
46/* http://fxr.watson.org/fxr/source/include/net/xfrm.h?v=linux-2.6#L840 */
47static inline bool cidr4_match(const struct in_addr addr,
48 const struct in_addr net, uint8_t bits) {
49 if (bits == 0) { // C99 6.5.7 (3): u32 << 32 is undefined behaviour
50 return true;
51 }
52 return !((addr.s_addr ^ net.s_addr) & htonl(0xFFFFFFFFu << (32 - bits)));
53}
54
55static inline bool cidr6_match(const struct in6_addr address,
56 const struct in6_addr network, uint8_t bits) {
57 //#ifdef LINUX
58 const uint32_t *a = address.s6_addr32;
59 const uint32_t *n = network.s6_addr32;
60 /*
61#else
62 const uint32_t *a = address.__u6_addr.__u6_addr32;
63 const uint32_t *n = network.__u6_addr.__u6_addr32;
64#endif
65*/
66 int bits_whole, bits_incomplete;
67 bits_whole = bits >> 5; // number of whole u32
68 bits_incomplete = bits & 0x1F; // number of bits in incomplete u32
69 if (bits_whole) {
70 if (memcmp(a, n, bits_whole << 2)) {
71 return false;
72 }
73 }
74 if (bits_incomplete) {
75 uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
76 if ((a[bits_whole] ^ n[bits_whole]) & mask) {
77 return false;
78 }
79 }
80 return true;
81}
82
83static inline int get_ip_version(const char *ip) {
84 struct in_addr out4;
85 struct in6_addr out6;
86 int res = inet_pton(AF_INET, ip, &out4);
87 if (0 == res) {
88 if (1 == inet_pton(AF_INET6, ip, &out6)) {
89 return AF_INET6;
90 } else {
91 return -1;
92 }
93 } else if (1 == res) {
94 return AF_INET;
95 } else {
96 return -1;
97 }
98}
99
100// TODO factorise a bit this function
101bool cidr_match(const char *ip, const sp_cidr *cidr) {
102 struct in_addr out4;
103 struct in6_addr out6;
104
105 switch (get_ip_version(ip)) {
106 case AF_INET:
107 if (AF_INET != cidr->ip_version) {
108 return false;
109 }
110 inet_pton(AF_INET, ip, &out4);
111 return cidr4_match(out4, cidr->ip.ipv4, cidr->mask);
112 case AF_INET6:
113 if (AF_INET6 != cidr->ip_version) {
114 return false;
115 }
116 inet_pton(AF_INET6, ip, &out6);
117 return cidr6_match(out6, cidr->ip.ipv6, cidr->mask);
118 default:
119 sp_log_err("cidr_match", "Weird ip (%s) family", ip);
120 break;
121 }
122 return false;
123}
124
125int get_ip_and_cidr(char *ip, sp_cidr *cidr) {
126 errno = 0;
127 char *mask = strchr(ip, '/');
128
129 if (NULL == mask) {
130 sp_log_err("config",
131 "'%s' isn't a valid network mask, it seems that you forgot a '/'.",
132 ip);
133 return -1;
134 }
135
136 if (sscanf(mask + 1, "%hhu", &(cidr->mask)) != 1) {
137 sp_log_err("config", "'%s' isn't a valid network mask.", mask + 1);
138 return -1;
139 }
140
141 ip[mask - ip] = '\0'; // NULL the '/' char
142
143 cidr->ip_version = get_ip_version(ip);
144
145 if (AF_INET == cidr->ip_version) {
146 if (cidr->mask > 32) {
147 sp_log_err("config", "'%d' isn't a valid ipv4 mask.", cidr->mask);
148 return -1;
149 }
150 inet_pton(AF_INET, ip, &(cidr->ip.ipv4));
151 } else if (AF_INET6 == cidr->ip_version) {
152 inet_pton(AF_INET6, ip, &(cidr->ip.ipv6));
153 } else {
154 return -1;
155 }
156
157 ip[mask - ip] = '/';
158 return 0;
159}
diff --git a/src/sp_network_utils.h b/src/sp_network_utils.h
new file mode 100644
index 0000000..6b6ce92
--- /dev/null
+++ b/src/sp_network_utils.h
@@ -0,0 +1,8 @@
1#ifndef SP_NETWORK_UTILS_H
2#define SP_NETWORK_UTILS_H
3
4int get_ip_and_cidr(char *, sp_cidr *);
5bool cidr_match(const char *, const sp_cidr *);
6void apply_mask_on_ip(char *, const char * const);
7
8#endif /*SP_NETWORK_UTILS_H*/
diff --git a/src/sp_unserialize.c b/src/sp_unserialize.c
new file mode 100644
index 0000000..b5b67b4
--- /dev/null
+++ b/src/sp_unserialize.c
@@ -0,0 +1,111 @@
1#include "php_snuffleupagus.h"
2
3ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
4
5PHP_FUNCTION(sp_serialize) {
6 void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS);
7
8 /* Call the original `serialize` function. */
9 if ((orig_handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook),
10 "serialize", 9))) {
11 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
12 } else {
13 sp_log_err("disabled_functions",
14 "Unable to find the pointer to the original function 'serialize' in "
15 "the hashtable.\n");
16 }
17
18 /* Compute the HMAC of the textual representation of the serialized data*/
19 zval func_name;
20 zval hmac;
21 zval params[3];
22
23 ZVAL_STRING(&func_name, "hash_hmac");
24 ZVAL_STRING(&params[0], "sha256");
25 params[1] = *return_value;
26 ZVAL_STRING(&params[2],
27 SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key);
28 call_user_function(CG(function_table), NULL, &func_name, &hmac, 3, params);
29
30 size_t len = Z_STRLEN_P(return_value) + Z_STRLEN(hmac);
31 zend_string *res = zend_string_alloc(len, 0);
32
33 memcpy(ZSTR_VAL(res), Z_STRVAL_P(return_value), Z_STRLEN_P(return_value));
34 memcpy(ZSTR_VAL(res) + Z_STRLEN_P(return_value), Z_STRVAL(hmac),
35 Z_STRLEN(hmac));
36 ZSTR_VAL(res)[len] = '\0';
37
38 /* Append the computed HMAC to the serialized data. */
39 return_value->value.str = res;
40 return;
41}
42
43PHP_FUNCTION(sp_unserialize) {
44 void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS);
45
46 char *buf = NULL;
47 char *serialized_str = NULL;
48 char *hmac = NULL;
49 zval expected_hmac;
50 size_t buf_len = 0;
51 zval *opts = NULL;
52
53 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &buf, &buf_len, &opts) ==
54 FAILURE) {
55 RETURN_FALSE;
56 }
57
58 /* 64 is the length of HMAC-256 */
59 if (buf_len < 64) {
60 sp_log_msg("unserialize", LOG_DROP, "The serialized object is too small.");
61 RETURN_FALSE;
62 }
63
64 hmac = buf + buf_len - 64;
65 serialized_str = ecalloc(sizeof(*serialized_str) * (buf_len - 64 + 1), 1);
66 memcpy(serialized_str, buf, buf_len - 64);
67
68 zval func_name;
69 ZVAL_STRING(&func_name, "hash_hmac");
70
71 zval params[3];
72 ZVAL_STRING(&params[0], "sha256");
73 ZVAL_STRING(&params[1], serialized_str);
74 ZVAL_STRING(&params[2],
75 SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key);
76 call_user_function(CG(function_table), NULL, &func_name, &expected_hmac, 3,
77 params);
78
79 unsigned int status = 0;
80 for (uint8_t i = 0; i < 64; i++) {
81 status |= (hmac[i] ^ (Z_STRVAL(expected_hmac))[i]);
82 }
83
84 if (0 == status) {
85 if ((orig_handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook),
86 "unserialize", 11))) {
87 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
88 }
89 } else {
90 if ( true == SNUFFLEUPAGUS_G(config).config_unserialize->simulation) {
91 sp_log_msg("unserialize", LOG_NOTICE, "Invalid HMAC for %s", serialized_str);
92 if ((orig_handler = zend_hash_str_find_ptr(SNUFFLEUPAGUS_G(sp_internal_functions_hook),
93 "unserialize", 11))) {
94 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
95 }
96 } else {
97 sp_log_msg("unserialize", LOG_DROP, "Invalid HMAC for %s", serialized_str);
98 }
99 }
100 efree(serialized_str);
101 return;
102}
103
104int hook_serialize(void) {
105 TSRMLS_FETCH();
106
107 HOOK_FUNCTION("serialize", sp_internal_functions_hook, PHP_FN(sp_serialize), false);
108 HOOK_FUNCTION("unserialize", sp_internal_functions_hook, PHP_FN(sp_unserialize), false);
109
110 return SUCCESS;
111}
diff --git a/src/sp_unserialize.h b/src/sp_unserialize.h
new file mode 100644
index 0000000..557c60c
--- /dev/null
+++ b/src/sp_unserialize.h
@@ -0,0 +1,9 @@
1#ifndef SP_UNSERIALIZE_H
2#define SP_UNSERIALIZE_H
3
4int hook_serialize(void);
5
6PHP_FUNCTION(sp_serialize);
7PHP_FUNCTION(sp_unserialize);
8
9#endif /* SP_UNSERIALIZE_H */
diff --git a/src/sp_upload_validation.c b/src/sp_upload_validation.c
new file mode 100644
index 0000000..bbd7eae
--- /dev/null
+++ b/src/sp_upload_validation.c
@@ -0,0 +1,92 @@
1#include "php_snuffleupagus.h"
2#include "rfc1867.h"
3
4ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus);
5
6#define EFREE_3(env) \
7 for (size_t i = 0; i < 4; i++) { \
8 efree(env[i]); \
9 }
10
11void hook_upload() {
12 sp_rfc1867_orig_callback = php_rfc1867_callback;
13 php_rfc1867_callback = sp_rfc1867_callback;
14}
15
16int sp_rfc1867_callback(unsigned int event, void *event_data, void **extra) {
17 int retval = SUCCESS;
18
19 if (sp_rfc1867_orig_callback) {
20 retval = sp_rfc1867_orig_callback(event, event_data, extra);
21 }
22
23 if (event == MULTIPART_EVENT_END) {
24 zend_string *file_key __attribute__((unused)) = NULL;
25 zval *file;
26 pid_t pid;
27
28 sp_log_debug(
29 "Got %d files",
30 zend_hash_num_elements(Z_ARRVAL(PG(http_globals)[TRACK_VARS_FILES])));
31
32 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(PG(http_globals)[TRACK_VARS_FILES]),
33 file_key, file) { // for each uploaded file
34
35 char *filename =
36 Z_STRVAL_P(zend_hash_str_find(Z_ARRVAL_P(file), "name", 4));
37 char *tmp_name =
38 Z_STRVAL_P(zend_hash_str_find(Z_ARRVAL_P(file), "tmp_name", 8));
39 size_t filesize =
40 Z_LVAL_P(zend_hash_str_find(Z_ARRVAL_P(file), "size", 4));
41 char *cmd[3] = {0};
42 char *env[5] = {0};
43
44 sp_log_debug("Filename: %s\nTmpname: %s\nSize: %d\nError: %d\nScript: %s",
45 filename, tmp_name, filesize,
46 Z_LVAL_P(zend_hash_str_find(Z_ARRVAL_P(file), "error", 5)),
47 SNUFFLEUPAGUS_G(config).config_upload_validation->script);
48
49 cmd[0] = SNUFFLEUPAGUS_G(config).config_upload_validation->script;
50 cmd[1] = tmp_name;
51 cmd[2] = NULL;
52
53 spprintf(&env[0], 0, "SP_FILENAME=%s", filename);
54 spprintf(&env[1], 0, "SP_REMOTE_ADDR=%s", sp_getenv("REMOTE_ADDR"));
55 spprintf(&env[2], 0, "SP_CURRENT_FILE=%s",
56 zend_get_executed_filename(TSRMLS_C));
57 spprintf(&env[3], 0, "SP_FILESIZE=%zu", filesize);
58 env[4] = NULL;
59
60 if ((pid = fork()) == 0) {
61 if (execve(SNUFFLEUPAGUS_G(config).config_upload_validation->script,
62 cmd, env) == -1) {
63 sp_log_err("upload_validation", "Could not call '%s' : %s",
64 SNUFFLEUPAGUS_G(config).config_upload_validation->script,
65 strerror(errno));
66 EFREE_3(env);
67 exit(1);
68 }
69 } else if (pid == -1) {
70 sp_log_err("upload_validation", "Could not fork process : %s\n",
71 strerror(errno));
72 EFREE_3(env);
73 continue;
74 }
75
76 EFREE_3(env);
77 int waitstatus;
78 wait(&waitstatus);
79 if (WEXITSTATUS(waitstatus) != 0) { // Nope
80 char *uri = sp_getenv("REQUEST_URI");
81 int sim = SNUFFLEUPAGUS_G(config).config_upload_validation->simulation;
82 sp_log_msg("upload_valiation", sim?LOG_NOTICE:LOG_DROP,
83 "The upload of %s on %s was rejected.", filename, uri?uri:"?");
84 if (!SNUFFLEUPAGUS_G(config).config_upload_validation->simulation) {
85 zend_bailout();
86 }
87 }
88 }
89 ZEND_HASH_FOREACH_END();
90 }
91 return retval;
92}
diff --git a/src/sp_upload_validation.h b/src/sp_upload_validation.h
new file mode 100644
index 0000000..3d59527
--- /dev/null
+++ b/src/sp_upload_validation.h
@@ -0,0 +1,9 @@
1#ifndef __SP_UPLOAD_VALIDATION_H__
2#define __SP_UPLOAD_VALIDATION_H__
3
4void hook_upload();
5
6int (*sp_rfc1867_orig_callback)(unsigned int event, void *event_data, void **extra);
7int sp_rfc1867_callback(unsigned int event, void *event_data, void **extra);
8
9#endif
diff --git a/src/sp_utils.c b/src/sp_utils.c
new file mode 100644
index 0000000..087f431
--- /dev/null
+++ b/src/sp_utils.c
@@ -0,0 +1,425 @@
1#include "php_snuffleupagus.h"
2
3#include <fcntl.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <unistd.h>
8
9static inline void _sp_log_err(const char* fmt, ...) {
10 char* msg;
11 va_list args;
12
13 va_start(args, fmt);
14 vspprintf(&msg, 0, fmt, args);
15 va_end(args);
16 php_log_err(msg);
17}
18
19void sp_log_msg(char const *feature, char const *level, const char* fmt, ...) {
20 char* msg;
21 va_list args;
22
23 va_start(args, fmt);
24 vspprintf(&msg, 0, fmt, args);
25 va_end(args);
26
27 char const * const client_ip = sp_getenv("REMOTE_ADDR");
28 _sp_log_err("[snuffleupagus][%s][%s][%s] %s", client_ip?client_ip:"0.0.0.0",
29 feature, level, msg);
30}
31
32int compute_hash(const char* const filename, char* file_hash) {
33 unsigned char buf[1024];
34 unsigned char digest[SHA256_SIZE];
35 PHP_SHA256_CTX context;
36 size_t n;
37
38 php_stream* stream =
39 php_stream_open_wrapper(filename, "rb", REPORT_ERRORS, NULL);
40 if (!stream) {
41 sp_log_err("hash_computation", "Can not open the file %s to compute its hash.\n", filename);
42 return -1;
43 }
44
45 PHP_SHA256Init(&context);
46 while ((n = php_stream_read(stream, (char*)buf, sizeof(buf))) > 0) {
47 PHP_SHA256Update(&context, buf, n);
48 }
49 PHP_SHA256Final(digest, &context);
50 php_stream_close(stream);
51 make_digest_ex(file_hash, digest, SHA256_SIZE);
52 return 0;
53}
54
55static void construct_filename(char* filename, const char* folder) {
56 time_t t = time(NULL);
57 struct tm* tm = localtime(&t); // FIXME use `localtime_r` instead
58 struct timeval tval;
59 struct stat st = {0};
60
61 if (-1 == stat(folder, &st)) {
62 mkdir(folder, 0700);
63 }
64
65 memcpy(filename, folder, strlen(folder));
66 strcat(filename, "sp_dump_");
67 strftime(filename + strlen(filename), 27, "%F_%T:", tm);
68 gettimeofday(&tval, NULL);
69 sprintf(filename + strlen(filename), "%04ld", tval.tv_usec);
70 strcat(filename, "_");
71
72 char* remote_addr = getenv("REMOTE_ADDR");
73 if (remote_addr) {
74 strcat(filename, remote_addr);
75 } else {
76 strcat(filename, "0.0.0.0");
77 }
78 strcat(filename, ".dump");
79}
80
81int sp_log_request(const char* folder) {
82 FILE* file;
83 const char* current_filename = zend_get_executed_filename(TSRMLS_C);
84 const int current_line = zend_get_executed_lineno(TSRMLS_C);
85 char filename[MAX_FOLDER_LEN] = {0};
86 const struct {
87 const char* str;
88 const int key;
89 } zones[] = {{"GET", TRACK_VARS_GET}, {"POST", TRACK_VARS_POST},
90 {"COOKIE", TRACK_VARS_COOKIE}, {"SERVER", TRACK_VARS_SERVER},
91 {"ENV", TRACK_VARS_ENV}, {NULL, 0}};
92
93 construct_filename(filename, folder);
94 if (NULL == (file = fopen(filename, "a"))) {
95 sp_log_err("request_logging", "Unable to open %s", filename);
96 return -1;
97 }
98
99 fprintf(file, "%s:%d\n", current_filename, current_line);
100 for (size_t i = 0; i < (sizeof(zones) / sizeof(zones[0])) - 1; i++) {
101 zval* variable_value;
102 zend_string* variable_key;
103 size_t params_len = strlen(zones[i].str) + 1;
104 char* param;
105 size_t size_max = 2048;
106
107 if (Z_TYPE(PG(http_globals)[zones[i].key]) == IS_UNDEF) {
108 continue;
109 }
110
111 const HashTable* ht = Z_ARRVAL(PG(http_globals)[zones[i].key]);
112
113 // Compute the size of the allocation
114 ZEND_HASH_FOREACH_STR_KEY_VAL(ht, variable_key, variable_value) {
115 params_len += snprintf(NULL, 0, "%s=%s&", ZSTR_VAL(variable_key),
116 Z_STRVAL_P(variable_value));
117 }
118 ZEND_HASH_FOREACH_END();
119
120 params_len = params_len>size_max?size_max:params_len;
121
122#define NCAT_AND_DEC(a, b, c) strncat(a, b, c); c -= strlen(b);
123
124 // Allocate and copy the data
125 // FIXME Why are we even allocating?
126 param = pecalloc(params_len, 1, 0);
127 NCAT_AND_DEC(param, zones[i].str, params_len);
128 NCAT_AND_DEC(param, ":", params_len);
129 ZEND_HASH_FOREACH_STR_KEY_VAL(ht, variable_key, variable_value) {
130 NCAT_AND_DEC(param, ZSTR_VAL(variable_key), params_len);
131 NCAT_AND_DEC(param, "=", params_len);
132 NCAT_AND_DEC(param, Z_STRVAL_P(variable_value), params_len);
133 NCAT_AND_DEC(param, "&", params_len);
134 }
135 ZEND_HASH_FOREACH_END();
136
137 param[strlen(param) - 1] = '\0';
138
139 fputs(param, file);
140 fputs("\n", file);
141 }
142 fclose(file);
143
144#undef CAT_AND_DEC
145 return 0;
146}
147
148static char *zv_str_to_char(zval *zv) {
149 zval copy;
150 char *ret;
151
152
153 ZVAL_ZVAL(&copy, zv, 1, 0);
154 // str = zend_string_dup(Z_STR_P(zv), 0);
155 for (size_t i = 0; i < Z_STRLEN(copy); i++) {
156 if (Z_STRVAL(copy)[i] == '\0') {
157 Z_STRVAL(copy)[i] = '0';
158 }
159 }
160 ret = estrdup(Z_STRVAL(copy));
161 // zend_string_release(str);
162 return ret;
163}
164
165
166char* sp_convert_to_string(zval* zv) {
167 switch (Z_TYPE_P(zv)) {
168 case IS_FALSE:
169 return estrdup("FALSE");
170 case IS_TRUE:
171 return estrdup("TRUE");
172 case IS_NULL:
173 return estrdup("NULL");
174 case IS_LONG: {
175 char *msg;
176 spprintf(&msg, 0, ZEND_LONG_FMT, Z_LVAL_P(zv));
177 return msg;
178 }
179 case IS_DOUBLE: {
180 char *msg;
181 spprintf(&msg, 0, "%f", Z_DVAL_P(zv));
182 return msg;
183 }
184 case IS_STRING:{
185 return zv_str_to_char(zv);
186 }
187 case IS_OBJECT:
188 return estrdup("OBJECT");
189 case IS_ARRAY:
190 return estrdup("ARRAY");
191 case IS_RESOURCE:
192 return estrdup("RESOURCE");
193 }
194 return estrdup("");
195}
196
197bool sp_match_value(const char* value, const char* to_match, const pcre* rx) {
198 if (to_match) {
199 if (0 == strcmp(value, to_match)) {
200 return true;
201 }
202 } else if (rx) {
203 int substrvec[30];
204 int ret = pcre_exec(rx, NULL, value, strlen(value), 0, 0, substrvec, 30);
205
206 if (ret < 0) {
207 if (ret != PCRE_ERROR_NOMATCH) {
208 sp_log_err("regexp", "Something went wrong with a regexp.");
209 return false;
210 }
211 return false;
212 }
213 return true;
214 }
215 return false;
216}
217
218void sp_log_disable(const char* restrict path, const char* restrict arg_name,
219 const char* restrict arg_value,
220 const sp_disabled_function* config_node) {
221 const char* dump = config_node->dump;
222 const char* alias = config_node->alias;
223 const int sim = config_node->simulation;
224 if (arg_name) {
225 if (alias) {
226 sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP,
227 "The call to the function '%s' in %s:%d has been disabled, "
228 "because its argument '%s' content (%s) matched the rule '%s'.",
229 path, zend_get_executed_filename(TSRMLS_C),
230 zend_get_executed_lineno(TSRMLS_C), arg_name, arg_value?arg_value:"?",
231 alias);
232 } else {
233 sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP,
234 "The call to the function '%s' in %s:%d has been disabled, "
235 "because its argument '%s' content (%s) matched a rule.",
236 path, zend_get_executed_filename(TSRMLS_C),
237 zend_get_executed_lineno(TSRMLS_C), arg_name,
238 arg_value?arg_value:"?");
239 }
240 } else {
241 if (alias) {
242 sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP,
243 "The call to the function '%s' in %s:%d has been disabled, "
244 "because of the the rule '%s'.",path,
245 zend_get_executed_filename(TSRMLS_C),
246 zend_get_executed_lineno(TSRMLS_C), alias);
247 } else {
248 sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP,
249 "The call to the function '%s' in %s:%d has been disabled.",
250 path, zend_get_executed_filename(TSRMLS_C),
251 zend_get_executed_lineno(TSRMLS_C));
252 }
253 }
254 if (dump) {
255 sp_log_request(config_node->dump);
256 }
257}
258
259void sp_log_disable_ret(const char* restrict path,
260 const char* restrict ret_value,
261 const sp_disabled_function* config_node) {
262 const char* dump = config_node->dump;
263 const char* alias = config_node->alias;
264 const int sim = config_node->simulation;
265 if (alias) {
266 sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP,
267 "The execution has been aborted in %s:%d, "
268 "because the function '%s' returned '%s', which matched the rule '%s'.",
269 zend_get_executed_filename(TSRMLS_C),
270 zend_get_executed_lineno(TSRMLS_C), path, ret_value?ret_value:"?", alias);
271 } else {
272 sp_log_msg("disabled_function", sim?LOG_NOTICE:LOG_DROP,
273 "The execution has been aborted in %s:%d, "
274 "because the return value (%s) of the function '%s' matched a rule.",
275 zend_get_executed_filename(TSRMLS_C),
276 zend_get_executed_lineno(TSRMLS_C), ret_value?ret_value:"?", path);
277 }
278 if (dump) {
279 sp_log_request(dump);
280 }
281}
282
283int sp_match_array_key(const zval* zv, const char* to_match, const pcre* rx) {
284 zend_string* key;
285 zval* value;
286 char* arg_value_str;
287
288 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zv), key, value) {
289 if (Z_TYPE_P(value) == IS_ARRAY) {
290 continue;
291 }
292 arg_value_str = sp_convert_to_string(value);
293 if (!sp_match_value(arg_value_str, to_match, rx)) {
294 efree(arg_value_str);
295 continue;
296 } else {
297 efree(arg_value_str);
298 return 1;
299 }
300 }
301 ZEND_HASH_FOREACH_END();
302
303 (void)key; // silence a compiler warning
304
305 return 0;
306}
307
308int sp_match_array_key_recurse(const zval* arr, sp_node_t* keys,
309 const char* to_match, const pcre* rx) {
310 zend_string* key;
311 zval* value;
312 sp_node_t* current = keys;
313 if (current == NULL) {
314 return 0;
315 }
316 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(arr), key, value) {
317 if (Z_TYPE_P(value) == IS_ARRAY && !strcmp(ZSTR_VAL(key), current->data)) {
318 return sp_match_array_key_recurse(value, current->next, to_match, rx);
319 }
320 if (!strcmp(ZSTR_VAL(key), current->data) && current->next == NULL) {
321 if (!to_match && !rx) {
322 return 1;
323 }
324 if (Z_TYPE_P(value) == IS_ARRAY) {
325 return sp_match_array_key(value, to_match, rx);
326 } else {
327 char *value_str = sp_convert_to_string(value);
328 if (sp_match_value(value_str, to_match, rx)) {
329 efree(value_str);
330 return 1;
331 } else {
332 efree (value_str);
333 return 0;
334 }
335 }
336 }
337 }
338 ZEND_HASH_FOREACH_END();
339 return 0;
340}
341
342zend_always_inline char* sp_getenv(char* var) {
343 if (sapi_module.getenv) {
344 return sapi_module.getenv(ZEND_STRL(var));
345 } else {
346 return getenv(var);
347 }
348}
349
350zend_always_inline int is_regexp_matching(const pcre* regexp, const char* str) {
351 int vec[30];
352 int ret = pcre_exec(regexp, NULL, str, strlen(str), 0, 0, vec, sizeof(vec));
353 if (ret < 0) {
354 if (ret != PCRE_ERROR_NOMATCH) {
355 sp_log_err("regexp", "Something went wrong with a regexp.");
356 }
357 return false;
358 }
359 return true;
360}
361
362int hook_function(const char* original_name, HashTable* hook_table,
363 void (*new_function)(INTERNAL_FUNCTION_PARAMETERS),
364 bool hook_execution_table) {
365 zend_internal_function* func;
366 HashTable *ht = hook_execution_table==true?EG(function_table):CG(function_table);
367
368 /* The `mb` module likes to hook functions, like strlen->mb_strlen,
369 * so we have to hook both of them. */
370 if (0 == strncmp(original_name, "mb_", 3)) {
371 CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN;
372 if (zend_hash_str_find(ht,
373 VAR_AND_LEN(original_name + 3))) {
374 hook_function(original_name + 3, hook_table, new_function, hook_execution_table);
375 }
376 } else { // TODO this can be moved somewhere else to gain some marginal perfs
377 CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN;
378 char* mb_name = pecalloc(strlen(original_name) + 3 + 1, 1, 0);
379 memcpy(mb_name, "mb_", 3);
380 memcpy(mb_name + 3, VAR_AND_LEN(original_name));
381 if (zend_hash_str_find(CG(function_table), VAR_AND_LEN(mb_name))) {
382 hook_function(mb_name, hook_table, new_function, hook_execution_table);
383 }
384 }
385
386 if ((func = zend_hash_str_find_ptr(CG(function_table),
387 VAR_AND_LEN(original_name)))) {
388 if (func->handler == new_function) {
389 /* Success !*/
390 } else if (zend_hash_str_add_new_ptr((hook_table),
391 VAR_AND_LEN(original_name),
392 func->handler) == NULL) {
393 sp_log_err("function_pointer_saving",
394 "Could not save function pointer for %s", original_name);
395 return FAILURE;
396 } else {
397 func->handler = new_function;
398 }
399 }
400 return SUCCESS;
401}
402
403int hook_regexp(const pcre* regexp, HashTable* hook_table,
404 void (*new_function)(INTERNAL_FUNCTION_PARAMETERS),
405 bool hook_execution_table) {
406 zend_string* key;
407 HashTable *ht = hook_execution_table==true?EG(function_table):CG(function_table);
408
409 ZEND_HASH_FOREACH_STR_KEY(ht, key) {
410 if (key) {
411 int vec[30];
412 int ret = pcre_exec(regexp, NULL, key->val, key->len, 0, 0, vec, 30);
413 if (ret < 0) { /* Error or no match*/
414 if (PCRE_ERROR_NOMATCH != ret) {
415 sp_log_err("pcre", "Runtime error with pcre, error code: %d", ret);
416 return FAILURE;
417 }
418 continue;
419 }
420 hook_function(key->val, hook_table, new_function, hook_execution_table);
421 }
422 }
423 ZEND_HASH_FOREACH_END();
424 return SUCCESS;
425}
diff --git a/src/sp_utils.h b/src/sp_utils.h
new file mode 100644
index 0000000..37dd2c0
--- /dev/null
+++ b/src/sp_utils.h
@@ -0,0 +1,68 @@
1#ifndef SP_UTILS_H
2#define SP_UTILS_H
3
4#include "sp_config.h"
5#include "sp_list.h"
6
7#if defined(__GNUC__)
8# if __GNUC__ >= 3
9# define sp_pure __attribute__((pure))
10# define sp_const __attribute__((const))
11# else
12# define sp_pure
13# define sp_const
14# endif
15#endif
16/* The dump filename are of the form
17 * `sp_dump_DATE_IPADDR.dump`, with:
18 * - DATE being the output of asctime, 26 chars long
19 * - IP_ADDR being an IP adress, with a maximum size of 15
20 *
21 * We keep one char for the terminal \0, and one for the leading slash.
22 */
23
24#define MAX_FOLDER_LEN \
25 PATH_MAX - 1 - sizeof("sp_dump_") - 26 - sizeof("_") - 15 - \
26 sizeof(".dump") - 1
27
28#define VAR_AND_LEN(var) var, strlen(var)
29
30#define SHA256_SIZE 32
31
32#define HOOK_FUNCTION(original_name, hook_table, new_function, execution) \
33 hook_function(original_name, SNUFFLEUPAGUS_G(hook_table), new_function, execution)
34
35#define HOOK_FUNCTION_BY_REGEXP(regexp, hook_table, new_function, execution) \
36 hook_regexp(regexp, SNUFFLEUPAGUS_G(hook_table), new_function, execution)
37
38#define LOG_NOTICE "notice"
39#define LOG_DROP "drop"
40#define LOG_DEBUG "debug"
41#define LOG_ERROR "error"
42
43#define sp_log_err(feature, ...) sp_log_msg(feature, LOG_ERROR, __VA_ARGS__)
44#ifdef SP_DEBUG
45 #define sp_log_debug(...) sp_log_msg("DEBUG", LOG_DEBUG, __VA_ARGS__)
46#else
47 #define sp_log_debug(...)
48#endif
49
50void sp_log_msg(char const *feature, char const *level, const char* fmt, ...);
51int compute_hash(const char *const filename, char *file_hash);
52char *sp_convert_to_string(zval *);
53bool sp_match_value(const char *, const char *, const pcre *);
54int sp_match_array_key(const zval *, const char *, const pcre *);
55int sp_match_array_key_recurse(const zval *, sp_node_t *, const char *,
56 const pcre *);
57void sp_log_disable(const char *restrict, const char *restrict,
58 const char *restrict, const sp_disabled_function *);
59void sp_log_disable_ret(const char *restrict, const char *restrict,
60 const sp_disabled_function *);
61char *sp_getenv(char *);
62int is_regexp_matching(const pcre *, const char *);
63int hook_function(const char *, HashTable *,
64 void (*)(INTERNAL_FUNCTION_PARAMETERS), bool);
65int hook_regexp(const pcre *, HashTable *,
66 void (*)(INTERNAL_FUNCTION_PARAMETERS), bool);
67
68#endif /* SP_UTILS_H */
diff --git a/src/tests/broken_conf.phpt b/src/tests/broken_conf.phpt
new file mode 100644
index 0000000..ae0ef6e
--- /dev/null
+++ b/src/tests/broken_conf.phpt
@@ -0,0 +1,10 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf.ini
7--FILE--
8--EXPECT--
9[snuffleupagus][0.0.0.0][config][error] Invalid configuration prefix for 'this is a broken line'.
10
diff --git a/src/tests/broken_conf2.phpt b/src/tests/broken_conf2.phpt
new file mode 100644
index 0000000..88a2232
--- /dev/null
+++ b/src/tests/broken_conf2.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf2.ini
7--FILE--
8--EXPECT--
9[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 0000000..75bc603
--- /dev/null
+++ b/src/tests/broken_conf_config_regexp.phpt
@@ -0,0 +1,10 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_config_regexp.ini
7--FILE--
8--EXPECT--
9[snuffleupagus][0.0.0.0][config][error] Failed to compile '*.': nothing to repeat.
10[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 0000000..2f3fe19
--- /dev/null
+++ b/src/tests/broken_conf_enable_disable.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Global strict mode
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/borken_conf_enable_disable.ini
7--FILE--
8--EXPECTF--
9[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 0000000..80e1b61
--- /dev/null
+++ b/src/tests/broken_conf_expecting_bool.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Bad boolean value in configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_expecting_bool.ini
7--FILE--
8--EXPECT--
9[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 0000000..e806337
--- /dev/null
+++ b/src/tests/broken_conf_expecting_int.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Bad integer value in configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_expecting_int.ini
7--FILE--
8--EXPECT--
9[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 0000000..515091b
--- /dev/null
+++ b/src/tests/broken_conf_invalid_cidr.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_invalid_cidr.ini
7--FILE--
8--EXPECT--
9[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 0000000..d20cfcd
--- /dev/null
+++ b/src/tests/broken_conf_invalid_cidr6.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_invalid_cidr6.ini
7--FILE--
8--EXPECT--
9[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 0000000..de70a05
--- /dev/null
+++ b/src/tests/broken_conf_invalid_cidr6_no_slash.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration, invalid cidr for ipv6 because there is no `/` in it
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_invalid_cidr6_no_slash.ini
7--FILE--
8--EXPECT--
9[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 0000000..47d4a5d
--- /dev/null
+++ b/src/tests/broken_conf_invalid_cidr6_too_big.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration, cidr for ipv6 is too big, that will `mod` to 25.
3(13337%128 = 25)
4--SKIPIF--
5<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
6--INI--
7sp.configuration_file={PWD}/config/broken_conf_invalid_cidr6_too_big.ini
8--FILE--
9--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 0000000..712f123
--- /dev/null
+++ b/src/tests/broken_conf_invalid_cidr_value.phpt
@@ -0,0 +1,11 @@
1--TEST--
2Broken configuration, invalid cidr value
3(13337%128 = 25)
4--SKIPIF--
5<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
6--INI--
7sp.configuration_file={PWD}/config/broken_conf_invalid_cidr_value.ini
8--FILE--
9--EXPECT--
10[snuffleupagus][0.0.0.0][error][error] There is an issue with the parsing of '"': it doesn't look like a valid string.
11[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 0000000..29d2ff5
--- /dev/null
+++ b/src/tests/broken_conf_invalid_type.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken conf with wrong type
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_invalid_type.ini
7--FILE--
8--EXPECTF--
9[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 0000000..c4334b9
--- /dev/null
+++ b/src/tests/broken_conf_line_empty_string.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Configuration line with an empty string
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_line_empty_string.ini
7--FILE--
8--EXPECT--
9[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 0000000..07c94e4
--- /dev/null
+++ b/src/tests/broken_conf_line_no_closing.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Configuration line without closing parenthese
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_line_no_closing.ini
7--FILE--
8--EXPECT--
9[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 0000000..8e82708
--- /dev/null
+++ b/src/tests/broken_conf_line_too_long.phpt
@@ -0,0 +1,10 @@
1--TEST--
2Line too long in configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_line_too_long.ini
7--FILE--
8--EXPECT--
9[snuffleupagus][0.0.0.0][config][error] The following line is too long: 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111);.
10[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 0000000..e877cfa
--- /dev/null
+++ b/src/tests/broken_conf_lots_of_quotes.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Configuration line with too many quotes
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_lots_of_quotes.ini
7--FILE--
8--EXPECT--
9[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 0000000..9de7e5a
--- /dev/null
+++ b/src/tests/broken_conf_mutually_exclusive.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive.ini
7--FILE--
8--EXPECT--
9[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 0000000..9d3ea36
--- /dev/null
+++ b/src/tests/broken_conf_mutually_exclusive2.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive2.ini
7--FILE--
8--EXPECT--
9[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 0000000..58686a3
--- /dev/null
+++ b/src/tests/broken_conf_mutually_exclusive3.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive3.ini
7--FILE--
8--EXPECT--
9[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 0000000..d854380
--- /dev/null
+++ b/src/tests/broken_conf_mutually_exclusive4.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive4.ini
7--FILE--
8--EXPECT--
9[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 0000000..a265c30
--- /dev/null
+++ b/src/tests/broken_conf_mutually_exclusive5.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive5.ini
7--FILE--
8--EXPECT--
9[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 0000000..d0cdb85
--- /dev/null
+++ b/src/tests/broken_conf_mutually_exclusive6.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive6.ini
7--FILE--
8--EXPECT--
9[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 0000000..c9a3513
--- /dev/null
+++ b/src/tests/broken_conf_mutually_exclusive7.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive7.ini
7--FILE--
8--EXPECT--
9[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 0000000..7c5baee
--- /dev/null
+++ b/src/tests/broken_conf_mutually_exclusive8.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_mutually_exclusive8.ini
7--FILE--
8--EXPECT--
9[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 0000000..1d1e112
--- /dev/null
+++ b/src/tests/broken_conf_no_closing_misc.phpt
@@ -0,0 +1,10 @@
1--TEST--
2Configuration line without closing parenthese, misc
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_no_closing_misc.ini
7--FILE--
8--EXPECT--
9[snuffleupagus][0.0.0.0][config][error] Missing closing ) in line 123.
10[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 0000000..5293791
--- /dev/null
+++ b/src/tests/broken_conf_weird_keyword.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Bad config, unknown keyword
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_weird_keyword.ini
7--FILE--
8--EXPECT--
9[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 0000000..b6324fe
--- /dev/null
+++ b/src/tests/broken_conf_wrong_quotes.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Configuration line with too many quotes
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_wrong_quotes.ini
7--FILE--
8--EXPECT--
9[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 0000000..338ca3a
--- /dev/null
+++ b/src/tests/broken_conf_wrong_type.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken conf with wrong type
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_conf_wrong_type.ini
7--FILE--
8--EXPECTF--
9[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 0000000..cbfef7d
--- /dev/null
+++ b/src/tests/broken_regexp.phpt
@@ -0,0 +1,9 @@
1--TEST--
2Broken regexp
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/broken_regexp.ini
7--FILE--
8--EXPECTF--
9[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 0000000..4e95294
--- /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 0000000..0595320
--- /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 0000000..fdb6b8f
--- /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 0000000..51c28b2
--- /dev/null
+++ b/src/tests/config/broken_conf_expecting_bool.ini
@@ -0,0 +1,5 @@
1 # this is an example of broken conf
2
3
4 ; this is another comment
5sp.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 0000000..8e2efea
--- /dev/null
+++ b/src/tests/config/broken_conf_expecting_int.ini
@@ -0,0 +1,2 @@
1sp.global.secret_key("abcdef");
2sp.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 0000000..0cdc695
--- /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 0000000..e5a120c
--- /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 0000000..e4cf835
--- /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 0000000..417dee7
--- /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 0000000..733e889
--- /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 0000000..b2cd8cd
--- /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 0000000..74d0e5a
--- /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 0000000..bcac291
--- /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 0000000..ed057a5
--- /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 0000000..dfd48e7
--- /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 0000000..af1d505
--- /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 0000000..29b21d4
--- /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 0000000..556de08
--- /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 0000000..d212ad4
--- /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 0000000..5b64079
--- /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 0000000..d08ee58
--- /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 0000000..645c26c
--- /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 0000000..b08ef57
--- /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 0000000..2cb79a8
--- /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 0000000..89e19be
--- /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 0000000..bf5e7f5
--- /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 0000000..c8cc949
--- /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 0000000..6ecca6a
--- /dev/null
+++ b/src/tests/config/broken_conf_wrong_type.ini
@@ -0,0 +1,5 @@
1sp.disable_functions.function("strpos").ret_type("undef").drop().alias("Return value is undef");
2sp.disable_functions.function("strpos").ret_type("null").drop().alias("Return value is null");
3sp.disable_functions.function("strpos").ret_type("object").drop().alias("Return value is object");
4sp.disable_functions.function("strpos").ret_type("reference").drop().alias("Return value is reference");
5sp.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 0000000..efad83e
--- /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 0000000..8e1f69a
--- /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 0000000..9f90601
--- /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 0000000..6a33437
--- /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 0000000..52a43ba
--- /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 0000000..b92f136
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_filename_r.ini
@@ -0,0 +1,2 @@
1sp.disable_functions.function_r("^system$").filename_r("\\.txt$").drop();
2sp.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 0000000..4d088d2
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_method.ini
@@ -0,0 +1,3 @@
1sp.disable_functions.function("AwesomeClass::method1").drop();
2sp.disable_functions.function("method2").drop();
3sp.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 0000000..3f7178e
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_name_r.ini
@@ -0,0 +1,2 @@
1sp.disable_functions.function_r("^not_system$").ret("42").drop();
2sp.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 0000000..2b433df
--- /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 0000000..d09b81b
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_namespace.ini
@@ -0,0 +1,2 @@
1sp.disable_functions.function("strcmp").drop();
2sp.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 0000000..7994583
--- /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 0000000..7363781
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_param.ini
@@ -0,0 +1,6 @@
1sp.disable_functions.function("system").param("command").value_r("^id$").alias("1").drop();
2sp.disable_functions.function("array_sum").param("array").value_r("^8$").alias("2").drop();
3sp.disable_functions.function("shell_exec").param("cmd").value("id").alias("3").drop();
4sp.disable_functions.function("shell_exec").param("cmd").value("bla").alias("4").drop();
5sp.disable_functions.function("strcmp").param("str1").value("bla").alias("5").drop().simulation();
6sp.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 0000000..f8d9f43
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_param_alias.ini
@@ -0,0 +1,2 @@
1sp.disable_functions.function("system").alias("1").drop();
2sp.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 0000000..e349b38
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_param_allow.ini
@@ -0,0 +1,3 @@
1sp.disable_functions.function("system").param("command").value("echo win").filename("test.php").drop();
2sp.disable_functions.function("system").param("command").value("echo win").allow();
3sp.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 0000000..7b71692
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_param_array.ini
@@ -0,0 +1,4 @@
1sp.disable_functions.function("foo").param("arr").value("abcd").alias("1").drop();
2sp.disable_functions.function("foo").param("arr[bla]").value("abcdef").alias("2").drop();
3sp.disable_functions.function("foo").param("arr[test]").alias("3").drop();
4sp.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 0000000..2552f0a
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_param_int.ini
@@ -0,0 +1,2 @@
1sp.disable_functions.function("foobar").param("id").value("42").drop();
2sp.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 0000000..d9f6692
--- /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 0000000..641bd0a
--- /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 0000000..7171a30
--- /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 0000000..474fada
--- /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 0000000..1884227
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_ret_allow.ini
@@ -0,0 +1,2 @@
1sp.disable_functions.function("strpos").hash("70b33f3eaf585b245640bb2c92445d0040b2bcb31395aa25dede9f2df4dbcbe8").allow();
2sp.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 0000000..e179819
--- /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 0000000..6f49177
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_ret_right_hash.ini
@@ -0,0 +1,4 @@
1sp.disable_functions.function("system").ret("1").drop();
2sp.disable_functions.function("system").ret("1337").hash("123456789597a81a2b862cdb49920e2cba2e5979a3fc374c58c803e8f5c99a10").drop();
3sp.disable_functions.function("system").ret("1338").hash("522a976fa597a81a2b862cdb49920e2cba2e5979a3fc374c58c803e8f5c99a10").drop();
4sp.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 0000000..ee46c4b
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_ret_simulation.ini
@@ -0,0 +1,3 @@
1sp.disable_functions.function("strpos").ret("0").simulation().drop();
2sp.disable_functions.function("stripos").ret("0").simulation().drop().alias("1");
3sp.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 0000000..fab68fa
--- /dev/null
+++ b/src/tests/config/config_disabled_functions_right_hash.ini
@@ -0,0 +1,3 @@
1sp.disable_functions.function("system").hash("1337c3ad8cf096272cd0e78768af3b11325f498de5c2c36f40adc43643af378a").allow();
2sp.disable_functions.function("system").hash("d259c3ad8cf096272cd0e78768af3b11325f498de5c2c36f40adc43643af378a").allow();
3sp.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 0000000..15cbccc
--- /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 0000000..710e863
--- /dev/null
+++ b/src/tests/config/config_encrypted_cookies.ini
@@ -0,0 +1,3 @@
1sp.global.secret_key("abcdef");
2sp.cookie_encryption.cookie("super_cookie").mask_ipv4(8).mask_ipv6(2);
3sp.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 0000000..88f2acf
--- /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 0000000..b9cd227
--- /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 0000000..f2c1699
--- /dev/null
+++ b/src/tests/config/config_serialize.ini
@@ -0,0 +1,2 @@
1sp.global.secret_key("abcdef");
2sp.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 0000000..7f015e0
--- /dev/null
+++ b/src/tests/config/config_serialize_sim.ini
@@ -0,0 +1,2 @@
1sp.global.secret_key("abcdef");
2sp.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 0000000..bc9d1f2
--- /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 0000000..bb1e432
--- /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 0000000..64d98dc
--- /dev/null
+++ b/src/tests/config/disabled_function_local_var.ini
@@ -0,0 +1,2 @@
1sp.disable_functions.function("phpinfo").var("b").value("1337").drop();
2sp.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 0000000..e0c87e1
--- /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 0000000..cf54164
--- /dev/null
+++ b/src/tests/config/disabled_functions.ini
@@ -0,0 +1,7 @@
1sp.disable_functions.function("system").drop();
2sp.disable_functions.function("vprintf").hash("123456789").drop();
3sp.disable_functions.function("printf").disable().drop();
4sp.disable_functions.function("printf").simulation().drop();
5sp.disable_functions.function("print").disable().drop(); # this is a comment
6sp.disable_functions.function_r("^var_dump$").drop();
7sp.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 0000000..9e527ba
--- /dev/null
+++ b/src/tests/config/disabled_functions_cidr.ini
@@ -0,0 +1,4 @@
1sp.disable_functions.function("system").drop().cidr("127.0.0.1/8");
2sp.disable_functions.function("printf").drop().cidr("10.0.0.1/8");
3sp.disable_functions.function("strpos").drop().cidr("2001:0db8:0000:0000:0000:ff00:0042:8329/24");
4sp.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 0000000..b6afd97
--- /dev/null
+++ b/src/tests/config/disabled_functions_mb.ini
@@ -0,0 +1,2 @@
1sp.disable_functions.function("strlen").drop();
2sp.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 0000000..2b769a9
--- /dev/null
+++ b/src/tests/config/disabled_functions_ret.ini
@@ -0,0 +1,5 @@
1sp.disable_functions.function("testFunction").ret("0").drop().disable();
2sp.disable_functions.function("strpos").ret("0").drop().filename_r(".*\\.php");
3sp.disable_functions.function_r("str[ia]pos").ret_r("^[^a-z]+$").drop();
4sp.disable_functions.function_r("stripos").ret_r("^[^a-z]+").drop();
5sp.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 0000000..56c8e57
--- /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 0000000..a1239d8
--- /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 0000000..6cccd4d
--- /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 0000000..e81cf2c
--- /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 0000000..b3ff050
--- /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 0000000..02a37dd
--- /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 0000000..20422e4
--- /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 0000000..ca2bce3
--- /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 0000000..bba1af9
--- /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 0000000..8c595f9
--- /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 0000000..b5ae154
--- /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 0000000..e69de29
--- /dev/null
+++ b/src/tests/config/empty.ini
diff --git a/src/tests/config/empty_conf.ini b/src/tests/config/empty_conf.ini
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tests/config/empty_conf.ini
diff --git a/src/tests/config/encryption_key_only.ini b/src/tests/config/encryption_key_only.ini
new file mode 100644
index 0000000..7de4438
--- /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 0000000..2bc2bdc
--- /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 0000000..2e68471
--- /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 0000000..89e19be
--- /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 0000000..0646134
--- /dev/null
+++ b/src/tests/config/upload_validation.ini
@@ -0,0 +1,2 @@
1sp.upload_validation.script("tests/upload_ko.sh");
2sp.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 0000000..7a638a1
--- /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 0000000..b15977f
--- /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 0000000..da56439
--- /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 0000000..24f81a5
--- /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 0000000..bdf0a57
--- /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 0000000..5df8db8
--- /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 0000000..e5eb0c6
--- /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 0000000..c4cacdc
--- /dev/null
+++ b/src/tests/data/upload_ko.sh
@@ -0,0 +1,2 @@
1#!/bin/sh
2exit 1;
diff --git a/src/tests/data/upload_no_exec.sh b/src/tests/data/upload_no_exec.sh
new file mode 100644
index 0000000..6b9cafa
--- /dev/null
+++ b/src/tests/data/upload_no_exec.sh
@@ -0,0 +1,2 @@
1#!/bin/sh
2exit 0;
diff --git a/src/tests/data/upload_ok.sh b/src/tests/data/upload_ok.sh
new file mode 100755
index 0000000..6b9cafa
--- /dev/null
+++ b/src/tests/data/upload_ok.sh
@@ -0,0 +1,2 @@
1#!/bin/sh
2exit 0;
diff --git a/src/tests/deny_writable_execution.phpt b/src/tests/deny_writable_execution.phpt
new file mode 100644
index 0000000..2870561
--- /dev/null
+++ b/src/tests/deny_writable_execution.phpt
@@ -0,0 +1,44 @@
1--TEST--
2Readonly execution attempt
3--SKIPIF--
4<?php
5if (!extension_loaded("snuffleupagus")) print "skip";
6
7$filename = __DIR__ . '/test.txt';
8
9@unlink($filename);
10
11file_put_contents($filename, 'a');
12chmod($filename, 0400);
13
14if (is_writable($filename)) print "skip";
15@unlink($filename);
16 ?>
17--INI--
18sp.configuration_file={PWD}/config/config_disable_writable.ini
19--FILE--
20<?php
21$dir = __DIR__;
22
23// just in case
24@unlink("$dir/non_writable_file.txt");
25@unlink("$dir/writable_file.txt");
26
27file_put_contents("$dir/non_writable_file.txt", '<?php echo "Code execution within a non-writable file.\n";');
28file_put_contents("$dir/writable_file.txt", '<?php echo "Code execution within a writable file.\n";');
29chmod("$dir/non_writable_file.txt", 0400);
30chmod("$dir/writable_file.txt", 0777);
31include "$dir/non_writable_file.txt";
32include "$dir/writable_file.txt";
33?>
34--EXPECTF--
35Code execution within a non-writable file.
36[snuffleupagus][0.0.0.0][readonly_exec][drop] Attempted execution of a writable file (%a/writable_file.txt).
37--CLEAN--
38<?php
39$dir = __DIR__;
40chmod("$dir/non_writable_file.txt", 0777);
41chmod("$dir/writable_file.txt", 0777);
42unlink("$dir/non_writable_file.txt");
43unlink("$dir/writable_file.txt");
44?> \ 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 0000000..6d1233b
--- /dev/null
+++ b/src/tests/deny_writable_execution_disabled.phpt
@@ -0,0 +1,32 @@
1--TEST--
2Readonly execution attempt
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disable_writable_disabled.ini
7--FILE--
8<?php
9$dir = __DIR__;
10
11// just in case
12@unlink("$dir/non_writable_file.txt");
13@unlink("$dir/writable_file.txt");
14
15file_put_contents("$dir/writable_file.txt", '<?php echo "Code execution within a writable file.\n";');
16file_put_contents("$dir/non_writable_file.txt", '<?php echo "Code execution within a non-writable file.\n";');
17chmod("$dir/writable_file.txt", 0777);
18chmod("$dir/non_writable_file.txt", 0400);
19include "$dir/writable_file.txt";
20include "$dir/non_writable_file.txt";
21?>
22--EXPECT--
23Code execution within a writable file.
24Code execution within a non-writable file.
25--CLEAN--
26<?php
27$dir = __DIR__;
28chmod("$dir/non_writable_file.txt", 0777);
29chmod("$dir/writable_file.txt", 0777);
30unlink("$dir/non_writable_file.txt");
31unlink("$dir/writable_file.txt");
32?> \ 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 0000000..3278be8
--- /dev/null
+++ b/src/tests/deny_writable_execution_simulation.phpt
@@ -0,0 +1,45 @@
1--TEST--
2Readonly execution attempt (simulation mode)
3--SKIPIF--
4<?php
5if (!extension_loaded("snuffleupagus")) print "skip";
6
7$filename = __DIR__ . '/test.txt';
8
9@unlink($filename);
10
11file_put_contents($filename, 'a');
12chmod($filename, 0400);
13
14if (is_writable($filename)) print "skip";;
15@unlink($filename);
16 ?>
17--INI--
18sp.configuration_file={PWD}/config/config_disable_writable_simulation.ini
19--FILE--
20<?php
21$dir = __DIR__;
22
23// just in case
24@unlink("$dir/non_writable_file.txt");
25@unlink("$dir/writable_file.txt");
26
27file_put_contents("$dir/writable_file.txt", '<?php echo "Code execution within a writable file.\n";');
28file_put_contents("$dir/non_writable_file.txt", '<?php echo "Code execution within a non-writable file.\n";');
29chmod("$dir/writable_file.txt", 0777);
30chmod("$dir/non_writable_file.txt", 0400);
31include "$dir/writable_file.txt";
32include "$dir/non_writable_file.txt";
33?>
34--EXPECTF--
35[snuffleupagus][0.0.0.0][readonly_exec][notice] Attempted execution of a writable file (%a/writable_file.txt).
36Code execution within a writable file.
37Code execution within a non-writable file.
38--CLEAN--
39<?php
40$dir = __DIR__;
41chmod("$dir/non_writable_file.txt", 0777);
42chmod("$dir/writable_file.txt", 0777);
43unlink("$dir/non_writable_file.txt");
44unlink("$dir/writable_file.txt");
45?> \ 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 0000000..47f3db3
--- /dev/null
+++ b/src/tests/disable_xxe_dom.phpt
@@ -0,0 +1,71 @@
1--TEST--
2Disable XXE
3--SKIPIF--
4<?php
5 if (!extension_loaded("snuffleupagus")) die "skip";
6 if (!extension_loaded("dom")) die "skip";
7 ?>
8--INI--
9extension=`php-config --extension-dir`/dom.so
10sp.configuration_file={PWD}/config/disable_xxe.ini
11--FILE--
12<?php
13$dir = __DIR__;
14$content = 'WARNING, external entity loaded!';
15file_put_contents('content.txt', $content);
16
17$xml = <<<EOD
18<?xml version="1.0"?>
19<!DOCTYPE root
20[
21<!ENTITY foo SYSTEM "file://$dir/content.txt">
22]>
23<test><testing>&foo;</testing></test>
24EOD;
25
26file_put_contents('content.xml', $xml);
27
28libxml_disable_entity_loader(true);
29$dom = new DOMDocument('1.0');
30$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT);
31printf("libxml_disable_entity to true: %s\n", $dom->getElementsByTagName('testing')->item(0)->nodeValue);
32
33libxml_disable_entity_loader(false);
34$dom = new DOMDocument('1.0');
35$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT);
36printf("libxml_disable_entity to false: %s\n", $dom->getElementsByTagName('testing')->item(0)->nodeValue);
37
38$xml = "<test><testing>foo</testing></test>";
39file_put_contents('content.xml', $xml);
40
41libxml_disable_entity_loader(false);
42$dom = new DOMDocument('1.0');
43$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT);
44printf("without xxe: %s", $dom->getElementsByTagName('testing')->item(0)->nodeValue);
45
46?>
47--EXPECTF--
48Warning: DOMDocument::loadXML(): I/O warning : failed to load external entity "file://%a/content.txt" in %a/disable_xxe_dom.php on line %d
49
50Warning: DOMDocument::loadXML(): Failure to process entity foo in Entity, line: %d in %a/disable_xxe_dom.php on line %d
51
52Warning: DOMDocument::loadXML(): Entity 'foo' not defined in Entity, line: %d in %a/disable_xxe_dom.php on line %d
53
54Notice: Trying to get property of non-object in %a/disable_xxe_dom.php on line %d
55libxml_disable_entity to true:
56
57Warning: DOMDocument::loadXML(): I/O warning : failed to load external entity "file://%a/content.txt" in %a/disable_xxe_dom.php on line %d
58
59Warning: DOMDocument::loadXML(): Failure to process entity foo in Entity, line: %d in %a/disable_xxe_dom.php on line %d
60
61Warning: DOMDocument::loadXML(): Entity 'foo' not defined in Entity, line: %d in %a/disable_xxe_dom.php on line %d
62
63Notice: Trying to get property of non-object in %a/disable_xxe_dom.php on line %d
64libxml_disable_entity to false:
65without xxe: foo
66--CLEAN--
67<?php
68$dir = __DIR__;
69unlink($dir . "content.xml");
70unlink($dir . "content.txt");
71?>
diff --git a/src/tests/disable_xxe_dom_disabled.phpt b/src/tests/disable_xxe_dom_disabled.phpt
new file mode 100644
index 0000000..b89b595
--- /dev/null
+++ b/src/tests/disable_xxe_dom_disabled.phpt
@@ -0,0 +1,56 @@
1--TEST--
2Disable XXE
3--SKIPIF--
4<?php
5 if (!extension_loaded("snuffleupagus")) die "skip";
6 if (!extension_loaded("dom")) die "skip";
7 ?>
8--INI--
9extension=`php-config --extension-dir`/dom.so
10sp.configuration_file={PWD}/config/disable_xxe_disable.ini
11--FILE--
12<?php
13$dir = __DIR__;
14$content = '<content>WARNING, external entity loaded!</content>';
15file_put_contents($dir . '/content.txt', $content);
16
17$xml = <<<EOD
18<?xml version="1.0"?>
19<!DOCTYPE root
20[
21<!ENTITY foo SYSTEM "file://$dir/content.txt">
22]>
23<test><testing>&foo;</testing></test>
24EOD;
25
26file_put_contents($dir . '/content.xml', $xml);
27
28libxml_disable_entity_loader(true);
29$dom = new DOMDocument('1.0');
30$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT);
31printf("libxml_disable_entity to true: %s\n", $dom->getElementsByTagName('testing')->item(0)->nodeValue);
32
33libxml_disable_entity_loader(false);
34$dom = new DOMDocument('1.0');
35$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT);
36printf("libxml_disable_entity to false: %s\n", $dom->getElementsByTagName('testing')->item(0)->nodeValue);
37
38$xml = "<test><testing>foo</testing></test>";
39file_put_contents('content.xml', $xml);
40
41libxml_disable_entity_loader(false);
42$dom = new DOMDocument('1.0');
43$dom->loadXML($xml, LIBXML_DTDATTR|LIBXML_DTDLOAD|LIBXML_NOENT);
44printf("without xxe: %s", $dom->getElementsByTagName('testing')->item(0)->nodeValue);
45
46?>
47--EXPECTF--
48libxml_disable_entity to true: WARNING, external entity loaded!
49libxml_disable_entity to false: WARNING, external entity loaded!
50without xxe: foo
51--CLEAN--
52<?php
53$dir = __DIR__;
54unlink($dir . "/content.xml");
55unlink($dir . "/content.txt");
56?>
diff --git a/src/tests/disable_xxe_simplexml.phpt b/src/tests/disable_xxe_simplexml.phpt
new file mode 100644
index 0000000..54404a3
--- /dev/null
+++ b/src/tests/disable_xxe_simplexml.phpt
@@ -0,0 +1,52 @@
1--TEST--
2Disable XXE
3--SKIPIF--
4<?php
5 if (!extension_loaded("snuffleupagus")) die "skip";
6 if (!extension_loaded("simplexml")) die "skip";
7 ?>
8--INI--
9extension=`php-config --extension-dir`/simplexml.so
10sp.configuration_file={PWD}/config/disable_xxe.ini
11--FILE--
12<?php
13$dir = __DIR__;
14$content = 'WARNING, external entity loaded!';
15file_put_contents('content.txt', $content);
16
17$xml = <<<EOD
18<?xml version="1.0"?>
19<!DOCTYPE root
20[
21<!ENTITY foo SYSTEM "file://$dir/content.txt">
22]>
23<test><testing>&foo;</testing></test>
24EOD;
25
26file_put_contents('content.xml', $xml);
27
28libxml_disable_entity_loader(true);
29$doc = new SimpleXMLElement($xml);
30printf("libxml_disable_entity to true: %s\n", $doc->testing);
31
32libxml_disable_entity_loader(false);
33$doc = new SimpleXMLElement($xml);
34printf("libxml_disable_entity to false: %s\n", $doc->testing);
35
36$xml = "<test><testing>foo</testing></test>";
37file_put_contents('content.xml', $xml);
38
39$doc = new SimpleXMLElement($xml);
40printf("without xxe: %s", $doc->testing);
41
42?>
43--EXPECT--
44libxml_disable_entity to true:
45libxml_disable_entity to false:
46without xxe: foo
47--CLEAN--
48<?php
49$dir = __DIR__;
50unlink($dir . "/content.xml");
51unlink($dir . "/content.txt");
52?>
diff --git a/src/tests/disable_xxe_simplexml_oop.phpt b/src/tests/disable_xxe_simplexml_oop.phpt
new file mode 100644
index 0000000..62762eb
--- /dev/null
+++ b/src/tests/disable_xxe_simplexml_oop.phpt
@@ -0,0 +1,52 @@
1--TEST--
2Disable XXE
3--SKIPIF--
4<?php
5 if (!extension_loaded("snuffleupagus")) die "skip";
6 if (!extension_loaded("simplexml")) die "skip";
7 ?>
8--INI--
9extension=`php-config --extension-dir`/simplexml.so
10sp.configuration_file={PWD}/config/disable_xxe.ini
11--FILE--
12<?php
13$dir = __DIR__;
14$content = 'WARNING, external entity loaded!';
15file_put_contents('content.txt', $content);
16
17$xml = <<<EOD
18<?xml version="1.0"?>
19<!DOCTYPE root
20[
21<!ENTITY foo SYSTEM "file://$dir/content.txt">
22]>
23<test><testing>&foo;</testing></test>
24EOD;
25
26file_put_contents('content.xml', $xml);
27
28libxml_disable_entity_loader(true);
29$doc = simplexml_load_string($xml);
30printf("libxml_disable_entity to true: %s\n", $doc->testing);
31
32libxml_disable_entity_loader(false);
33$doc = simplexml_load_string($xml);
34printf("libxml_disable_entity to false: %s\n", $doc->testing);
35
36$xml = "<test><testing>foo</testing></test>";
37file_put_contents('content.xml', $xml);
38
39$doc = simplexml_load_string($xml);
40printf("without xxe: %s", $doc->testing);
41
42?>
43--EXPECT--
44libxml_disable_entity to true:
45libxml_disable_entity to false:
46without xxe: foo
47--CLEAN--
48<?php
49$dir = __DIR__;
50unlink($dir . "/content.xml");
51unlink($dir . "/content.txt");
52?>
diff --git a/src/tests/disable_xxe_xml_parse.phpt b/src/tests/disable_xxe_xml_parse.phpt
new file mode 100644
index 0000000..944bc38
--- /dev/null
+++ b/src/tests/disable_xxe_xml_parse.phpt
@@ -0,0 +1,104 @@
1--TEST--
2Disable XXE
3--SKIPIF--
4<?php
5 if (!extension_loaded("snuffleupagus")) die "skip";
6 if (!extension_loaded("xml")) die "skip";
7 ?>
8--INI--
9extension=`php-config --extension-dir`/xml.so
10sp.configuration_file={PWD}/config/disable_xxe.ini
11--FILE--
12<?php
13$dir = __DIR__;
14$content = 'WARNING, external entity loaded!';
15file_put_contents('content.txt', $content);
16
17$xml = <<<EOD
18<?xml version="1.0"?>
19<!DOCTYPE root
20[
21<!ENTITY foo SYSTEM "file://$dir/content.txt">
22]>
23<test><testing>&foo;</testing></test>
24EOD;
25
26file_put_contents('content.xml', $xml);
27
28function create_parser() {
29 $parser = xml_parser_create();
30 xml_set_element_handler(
31 $parser,
32 function($parser, $name, array $attributes) {
33 var_dump($name);
34 echo "\n";
35 var_dump($attributes);
36 },
37 function($parser, $name) {
38 var_dump($name);
39 }
40 );
41
42 xml_set_character_data_handler(
43 $parser,
44 function ($parser, $text){
45 echo 'text' . $text;
46 }
47 );
48
49 return $parser;
50}
51
52libxml_disable_entity_loader(true);
53$parser = create_parser();
54$doc = xml_parse($parser, $xml, true);
55xml_parser_free($parser);
56
57libxml_disable_entity_loader(false);
58$parser = create_parser();
59$doc = xml_parse($parser, $xml, true);
60xml_parser_free($parser);
61
62$xml = "<test><testing>foo</testing></test>";
63file_put_contents('content.xml', $xml);
64$parser = create_parser();
65$doc = xml_parse($parser, $xml, true);
66xml_parser_free($parser);
67
68--EXPECT--
69string(4) "TEST"
70
71array(0) {
72}
73string(7) "TESTING"
74
75array(0) {
76}
77string(7) "TESTING"
78string(4) "TEST"
79string(4) "TEST"
80
81array(0) {
82}
83string(7) "TESTING"
84
85array(0) {
86}
87string(7) "TESTING"
88string(4) "TEST"
89string(4) "TEST"
90
91array(0) {
92}
93string(7) "TESTING"
94
95array(0) {
96}
97textfoostring(7) "TESTING"
98string(4) "TEST"
99--CLEAN--
100<?php
101$dir = __DIR__;
102unlink($dir . "/content.xml");
103unlink($dir . "/content.txt");
104?>
diff --git a/src/tests/disabled_function_local_var.phpt b/src/tests/disabled_function_local_var.phpt
new file mode 100644
index 0000000..3142039
--- /dev/null
+++ b/src/tests/disabled_function_local_var.phpt
@@ -0,0 +1,24 @@
1--TEST--
2Disable functions - match on a local variable
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_function_local_var.ini
7--FILE--
8<?php
9$a = 1338;
10function test(){
11 echo strlen("id") . "\n";
12}
13echo "Value of a: $a\n";
14test();
15
16$a = 1337;
17echo "Value of a: $a\n";
18test();
19?>
20--EXPECTF--
21Value of a: 1338
222
23Value of a: 1337
24[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 0000000..d41897a
--- /dev/null
+++ b/src/tests/disabled_function_super_global_var.phpt
@@ -0,0 +1,20 @@
1--TEST--
2Disable functions - match on a super global
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_function_super_global_var.ini
7--GET--
8bla=test
9--FILE--
10<?php
11function test(){
12 echo strlen($_GET['bla']) . "\n";
13}
14test();
15$_GET['bla'] = 'test2';
16test();
17?>
18--EXPECTF--
194
20[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 0000000..37da911
--- /dev/null
+++ b/src/tests/disabled_functions.phpt
@@ -0,0 +1,21 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions.ini
7--FILE--
8<?php
9system("id");
10printf("printf in simulation mode\n");
11print("print in disabled mode\n");
12var_dump("this is a super test");
13echo strpos("pouet", "o");
14?>
15--EXPECTF--
16[snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'system' in %a/tests/disabled_functions.php:%d has been disabled.
17[snuffleupagus][0.0.0.0][disabled_function][notice] The call to the function 'printf' in %a/tests/disabled_functions.php:%d has been disabled.
18printf in simulation mode
19print in disabled mode
20[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.
211
diff --git a/src/tests/disabled_functions_cidr.phpt b/src/tests/disabled_functions_cidr.phpt
new file mode 100644
index 0000000..5b13107
--- /dev/null
+++ b/src/tests/disabled_functions_cidr.phpt
@@ -0,0 +1,18 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--ENV--
6return <<<EOF
7REMOTE_ADDR=127.0.0.1
8EOF;
9--INI--
10sp.configuration_file={PWD}/config/disabled_functions_cidr.ini
11--FILE--
12<?php
13system("echo 42");
14printf("1337");
15?>
16--EXPECTF--
17[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.
181337
diff --git a/src/tests/disabled_functions_cidr_6.phpt b/src/tests/disabled_functions_cidr_6.phpt
new file mode 100644
index 0000000..f2c5f5a
--- /dev/null
+++ b/src/tests/disabled_functions_cidr_6.phpt
@@ -0,0 +1,18 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--ENV--
6return <<<EOF
7REMOTE_ADDR=2001:0db8:0000:0000:0000:ff00:0042:8328
8EOF;
9--INI--
10sp.configuration_file={PWD}/config/disabled_functions_cidr.ini
11--FILE--
12<?php
13strpos("a", "b");
14printf(1337);
15?>
16--EXPECTF--
17[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.
181337
diff --git a/src/tests/disabled_functions_filename_r.phpt b/src/tests/disabled_functions_filename_r.phpt
new file mode 100644
index 0000000..ed46802
--- /dev/null
+++ b/src/tests/disabled_functions_filename_r.phpt
@@ -0,0 +1,14 @@
1--TEST--
2Disable functions - filename regexp
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_filename_r.ini
7--FILE--
8<?php
9system("echo 42");
10shell_exec("echo 43");
11?>
12--EXPECTF--
1342
14[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 0000000..7089063
--- /dev/null
+++ b/src/tests/disabled_functions_mb.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_mb.ini
7--FILE--
8<?php
9echo strlen("id");
10?>
11--EXPECTF--
12[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 0000000..33651b7
--- /dev/null
+++ b/src/tests/disabled_functions_method.phpt
@@ -0,0 +1,29 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_method.ini
7--FILE--
8<?php
9class AwesomeClass {
10 function method1($a) {
11 echo "method1:" . $a . "\n";
12 }
13 function method2($a) {
14 echo "method2:" . $a . "\n";
15 }
16 function method3($a) {
17 echo "method3:" . $a . "\n";
18 }
19}
20
21$c = new AwesomeClass();
22$c->method1("pif");
23$c->method2("paf");
24$c->method3("pouet");
25?>
26--EXPECTF--
27[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.
28method2:paf
29[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 0000000..0e29abb
--- /dev/null
+++ b/src/tests/disabled_functions_name_r.phpt
@@ -0,0 +1,15 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_name_r.ini
7--FILE--
8<?php
9system("echo 42");
10system("echo 1337");
11?>
12--EXPECTF--
1342
141337
15[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 0000000..c5b24d6
--- /dev/null
+++ b/src/tests/disabled_functions_name_type.phpt
@@ -0,0 +1,14 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_name_type.ini
7--FILE--
8<?php
9echo strcmp("pouet", "pouet") . "\n";
10echo strcmp([1,23], "pouet") . "\n";
11?>
12--EXPECTF--
130
14[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 0000000..72c7d0b
--- /dev/null
+++ b/src/tests/disabled_functions_namespace.phpt
@@ -0,0 +1,31 @@
1--TEST--
2Disable functions: namespaces support isn't implemented now
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_namespace.ini
7--FILE--
8<?php
9namespace my_super_namespace {
10 function my_function() {
11 echo "1\n";
12 }
13}
14namespace my_second_namespace {
15 function my_function() {
16 echo "2\n";
17 }
18}
19namespace {
20 function my_function() {
21 echo "3\n";
22 }
23\strcmp("1", "2");
24\my_super_namespace\my_function();
25\my_second_namespace\my_function();
26my_function();
27}
28?>
29--XFAIL--
30--EXPECTF--
31[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 0000000..cb13413
--- /dev/null
+++ b/src/tests/disabled_functions_noconf.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/empty.ini
7--FILE--
8<?php
9echo strpos("pouet", "o");
10?>
11--EXPECT--
121
diff --git a/src/tests/disabled_functions_nul_byte.phpt b/src/tests/disabled_functions_nul_byte.phpt
new file mode 100644
index 0000000..95e87de
--- /dev/null
+++ b/src/tests/disabled_functions_nul_byte.phpt
@@ -0,0 +1,15 @@
1--TEST--
2Disable functions with nul byte
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_nul_byte.ini
7--FILE--
8<?php
9system("\0id");
10system("id");
11
12?>
13--EXPECTF--
14[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.
15[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 0000000..2309217
--- /dev/null
+++ b/src/tests/disabled_functions_param.phpt
@@ -0,0 +1,24 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_param.ini
7--FILE--
8<?php
9system("id");
10system("echo win");
11var_dump(array_sum([1,2,3,4,5]));
12shell_exec("id");
13echo shell_exec("echo 42");
14strcmp("bla", "ble");
15strncmp("bla", "ble", 2);
16?>
17--EXPECTF--
18[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'.
19win
20int(15)
21[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'.
2242
23[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'.
24[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 0000000..fe3d1c1
--- /dev/null
+++ b/src/tests/disabled_functions_param_alias.phpt
@@ -0,0 +1,14 @@
1--TEST--
2Disable functions - alias
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_param_alias.ini
7--FILE--
8<?php
9system("id");
10shell_exec("id");
11?>
12--EXPECTF--
13[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'.
14[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 0000000..b6ff01a
--- /dev/null
+++ b/src/tests/disabled_functions_param_allow.phpt
@@ -0,0 +1,14 @@
1--TEST--
2Disable functions - allow
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_param_allow.ini
7--FILE--
8<?php
9system("echo win");
10system("id");
11?>
12--EXPECTF--
13win
14[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 0000000..6596d1a
--- /dev/null
+++ b/src/tests/disabled_functions_param_array.phpt
@@ -0,0 +1,37 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_param_array.ini
7--FILE--
8<?php
9function foo($arr) {
10 echo $arr["a"]."\n";
11}
12$a=Array("a"=>"test1");
13foo($a);
14$a=Array("a"=>"abcd");
15foo($a);
16$a=Array("a"=>"abcde");
17foo($a);
18$a=Array("bla"=>"abcdef");
19foo($a);
20$a=Array("bla"=>"aaa", "a"=>"eee" );
21foo($a);
22$a=Array("test"=>"aaa", "a"=>"fff" );
23foo($a);
24$a=Array("test2"=>Array("foo"=>Array("lol"=>"bbb")), "a"=>"cccc");
25foo($a);
26$a=Array("test2"=>Array("foo"=>Array("lol"=>"aaa")), "a"=>"dddd");
27foo($a);
28?>
29--EXPECTF--
30test1
31[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'.
32abcde
33[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'.
34eee
35[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'.
36cccc
37[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 0000000..3b2cc08
--- /dev/null
+++ b/src/tests/disabled_functions_param_int.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_param_int.ini
7--FILE--
8<?php
9function foobar($id) {
10 echo $id."\n";
11}
12foobar(1);
13foobar(42);
14foobar(1337);
15foobar(13374242);
16foobar(0x2A);
17foobar("10");
18?>
19--EXPECTF--
201
21[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.
22[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.
23[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.
24[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.
2510
diff --git a/src/tests/disabled_functions_param_r.phpt b/src/tests/disabled_functions_param_r.phpt
new file mode 100644
index 0000000..3708881
--- /dev/null
+++ b/src/tests/disabled_functions_param_r.phpt
@@ -0,0 +1,14 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_param_r.ini
7--FILE--
8<?php
9system("id");
10system("echo win");
11?>
12--EXPECTF--
13[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.
14win
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 0000000..7cbdc0f
--- /dev/null
+++ b/src/tests/disabled_functions_param_str_representation.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Disable functions - casting various types to string internally
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_param_str_representation.ini
7--FILE--
8<?php
9echo var_export(true) . "\n";
10echo var_export(false) . "\n";
11echo var_export(null) . "\n";
12echo var_export(1) . "\n";
13echo var_export(1.0) . "\n";
14function f(&$a) {
15 echo var_export($a) . "\n";
16}
17$a = 123; f($a);
18?>
19--EXPECTF--
20true
21false
22NULL
231
241.0
25123
diff --git a/src/tests/disabled_functions_parse_class.phpt b/src/tests/disabled_functions_parse_class.phpt
new file mode 100644
index 0000000..af9ed88
--- /dev/null
+++ b/src/tests/disabled_functions_parse_class.phpt
@@ -0,0 +1,22 @@
1--TEST--
2Disable functions - Parsing of an Object as a return value of a function
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_ret.ini
7--FILE--
8<?php
9/*
10Because Snuffleupagus used to cast everything with the `zval_get_string` function,
11this sometimes raised exceptions, because PHP is awful.
12 */
13class Bob {
14 function a() {
15 return new StdClass;
16 }
17}
18$b = new Bob;
19echo ($b->a() instanceof StdClass)?'Y':'N';
20?>
21--EXPECT--
22Y
diff --git a/src/tests/disabled_functions_require.phpt b/src/tests/disabled_functions_require.phpt
new file mode 100644
index 0000000..1eedde4
--- /dev/null
+++ b/src/tests/disabled_functions_require.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Disable functions - Require
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_require.ini
7--FILE--
8<?php
9$dir = __DIR__;
10file_put_contents($dir . '/test.meh', "");
11file_put_contents($dir . '/test.bla', "");
12require $dir . '/test.meh';
13require $dir . '/test.bla';
14echo "1337";
15?>
16--XFAIL--
17PHP doesn't replace the format string, so the test is failing.
18--EXPECTF--
19[snuffleupagus][0.0.0.0][include][drop] Inclusion of a forbidden file (%a/test.bla)
20--CLEAN--
21<?php
22$dir = __DIR__;
23unlink($dir . '/test.meh');
24unlink($dir . '/test.bla');
25?>
diff --git a/src/tests/disabled_functions_ret.phpt b/src/tests/disabled_functions_ret.phpt
new file mode 100644
index 0000000..b64bf70
--- /dev/null
+++ b/src/tests/disabled_functions_ret.phpt
@@ -0,0 +1,13 @@
1--TEST--
2Disable functions check on `ret`.
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_ret.ini
7--FILE--
8<?php
9echo strpos("pouet", "p");
10echo stripos("pouet", "p");
11?>
12--EXPECTF--
13[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 0000000..b713201
--- /dev/null
+++ b/src/tests/disabled_functions_ret2.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Disable functions check on `ret`.
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_ret.ini
7--FILE--
8<?php
9echo stripos("pouet", "p");
10?>
11--EXPECTF--
12[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 0000000..d5f96d0
--- /dev/null
+++ b/src/tests/disabled_functions_ret3.phpt
@@ -0,0 +1,22 @@
1--TEST--
2Disable functions check on `ret`.
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_ret.ini
7--FILE--
8<?php
9class Bob {
10 function a() {
11 echo("We're in function `a`.\n");
12 return 1;
13 }
14}
15$b = new Bob();
16echo "`a` returned: " . $b->a() . ".\n";
17echo("We're at the end of the execution.\n");
18?>
19--EXPECTF--
20We're in function `a`.
21`a` returned: 1.
22We'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 0000000..1690995
--- /dev/null
+++ b/src/tests/disabled_functions_ret_allow.phpt
@@ -0,0 +1,13 @@
1--TEST--
2Disable functions check on `ret`.
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_ret_allow.ini
7--FILE--
8<?php
9echo strpos("pouet", "p");
10echo stripos("pouet", "p");
11?>
12--EXPECT--
1300 \ 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 0000000..881a006
--- /dev/null
+++ b/src/tests/disabled_functions_ret_allow_value.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Disable functions check on `ret` allowed
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_ret_allow_value.ini
7--FILE--
8<?php
9echo strpos("pouet", "p");
10?>
11--EXPECT--
120
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 0000000..e0d8b5b
--- /dev/null
+++ b/src/tests/disabled_functions_ret_right_hash.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_ret_right_hash.ini
7--FILE--
8<?php
9system("echo $((1 + 1336))");
10?>
11--EXPECTF--
121337
diff --git a/src/tests/disabled_functions_ret_simulation.phpt b/src/tests/disabled_functions_ret_simulation.phpt
new file mode 100644
index 0000000..58af3a9
--- /dev/null
+++ b/src/tests/disabled_functions_ret_simulation.phpt
@@ -0,0 +1,18 @@
1--TEST--
2Disable functions check on `ret` simulation
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_ret_simulation.ini
7--FILE--
8<?php
9echo strpos("pouet", "p") . "\n";
10echo stripos("pouet", "p") . "\n";
11strcmp("p", "p") . "\n";
12?>
13--EXPECTF--
14[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.
150
16[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'.
170
18[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 0000000..f1c6e4c
--- /dev/null
+++ b/src/tests/disabled_functions_ret_type.phpt
@@ -0,0 +1,16 @@
1--TEST--
2Disable functions check on `ret` by type matching on boolean
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_ret_type.ini
7--FILE--
8<?php
9echo strpos("pouet", "p") . "\n";
10echo "1337\n";
11echo strpos("pouet", "123");
12?>
13--EXPECTF--
140
151337
16[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 0000000..b7942e1
--- /dev/null
+++ b/src/tests/disabled_functions_ret_type_double.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Disable functions check on `ret` by type matching (double).
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_ret_type_double.ini
7--FILE--
8<?php
9echo cos(0.5) . "\n";
10?>
11--EXPECTF--
12[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 0000000..b841c64
--- /dev/null
+++ b/src/tests/disabled_functions_ret_type_long.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Disable functions check on `ret` by type matching (long).
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_ret_type_long.ini
7--FILE--
8<?php
9echo strlen("pouet") . "\n";
10?>
11--EXPECTF--
12[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 0000000..4ceb610
--- /dev/null
+++ b/src/tests/disabled_functions_ret_type_resource.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Disable functions check on `ret` by type matching (resource).
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_ret_type_resource.ini
7--FILE--
8<?php
9echo fopen("/etc/passwd", "r");
10?>
11--EXPECTF--
12[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 0000000..8c48b1d
--- /dev/null
+++ b/src/tests/disabled_functions_ret_type_str.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Disable functions check on `ret` by type matching (string).
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_ret_type_str.ini
7--FILE--
8<?php
9echo substr("pouet", 3) . "\n";
10?>
11--EXPECTF--
12[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 0000000..a5eae38
--- /dev/null
+++ b/src/tests/disabled_functions_ret_type_true.phpt
@@ -0,0 +1,16 @@
1--TEST--
2Disable functions check on `ret` by type matching (true).
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_ret_type_true.ini
7--FILE--
8<?php
9var_dump(is_numeric("pouet")) . "\n";
10echo "1337\n";
11echo is_numeric("1234") . "\n";
12?>
13--EXPECTF--
14bool(false)
151337
16[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 0000000..8a02b29
--- /dev/null
+++ b/src/tests/disabled_functions_ret_val.phpt
@@ -0,0 +1,14 @@
1--TEST--
2Disable functions ret val
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_retval.ini
7--FILE--
8<?php
9echo str_repeat("fufu",1)."\n";
10echo str_repeat("fufufu",1);
11?>
12--EXPECTF--
13fufu
14[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 0000000..1054b70
--- /dev/null
+++ b/src/tests/disabled_functions_ret_val_rx.phpt
@@ -0,0 +1,14 @@
1--TEST--
2Disable functions ret val rx
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/disabled_functions_retval_rx.ini
7--FILE--
8<?php
9echo str_repeat("fufu",1)."\n";
10echo str_repeat("fufufu",1);
11?>
12--EXPECTF--
13fufu
14[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 0000000..f3c5fb3
--- /dev/null
+++ b/src/tests/disabled_functions_right_hash.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_right_hash.ini
7--FILE--
8<?php
9system("echo $((1 + 1336))");
10?>
11--EXPECTF--
121337
diff --git a/src/tests/disabled_functions_runtime.phpt b/src/tests/disabled_functions_runtime.phpt
new file mode 100644
index 0000000..1c6a141
--- /dev/null
+++ b/src/tests/disabled_functions_runtime.phpt
@@ -0,0 +1,31 @@
1--TEST--
2Disable functions - runtime inclusion
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_param_runtime.ini
7--FILE--
8<?php
9
10$dir = __DIR__;
11$content = '<?php function test($param) { echo $param . "\n"; }';
12file_put_contents('file_to_include1.php', $content);
13file_put_contents('file_to_include2.php', $content);
14
15if (rand() % 2) {
16 include "file_to_include1.php";
17} else {
18 include "file_to_include2.php";
19}
20
21test('1338');test('1337');
22
23?>
24--EXPECTF--
251338
26[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.
27--CLEAN--
28<?php
29unlink("file_to_include1.php");
30unlink("file_to_include2.php");
31?>
diff --git a/src/tests/disabled_functions_zero_cidr.phpt b/src/tests/disabled_functions_zero_cidr.phpt
new file mode 100644
index 0000000..35d187a
--- /dev/null
+++ b/src/tests/disabled_functions_zero_cidr.phpt
@@ -0,0 +1,18 @@
1--TEST--
2Disable functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--ENV--
6return <<<EOF
7REMOTE_ADDR=127.0.0.1
8EOF;
9--INI--
10sp.configuration_file={PWD}/config/disabled_functions_zero_cidr.ini
11--FILE--
12<?php
13system("echo 42");
14printf("1337");
15?>
16--EXPECTF--
17[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.
181337
diff --git a/src/tests/disabled_option.phpt b/src/tests/disabled_option.phpt
new file mode 100644
index 0000000..8bc7e39
--- /dev/null
+++ b/src/tests/disabled_option.phpt
@@ -0,0 +1,16 @@
1--TEST--
2Harden rand
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_rand_harden_disabled.ini
7--FILE--
8<?php
9srand(0);
10echo rand(0,100)."\n";
11srand(0);
12echo rand(0,100)."\n";
13?>
14--EXPECT--
1584
1684
diff --git a/src/tests/disabled_user_functions.phpt b/src/tests/disabled_user_functions.phpt
new file mode 100644
index 0000000..8952d43
--- /dev/null
+++ b/src/tests/disabled_user_functions.phpt
@@ -0,0 +1,15 @@
1--TEST--
2Disabled user-created functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_user_functions.ini
7--FILE--
8<?php
9function my_super_function() {
10 echo 1;
11}
12my_super_function();
13?>
14--EXPECTF--
15[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 0000000..a752def
--- /dev/null
+++ b/src/tests/dump_request.phpt
@@ -0,0 +1,39 @@
1--TEST--
2Dump request
3--SKIPIF--
4<?php
5if (!extension_loaded("snuffleupagus")) {
6 print "skip";
7}
8
9foreach (glob("./tests/dump_results/*.dump") as $dump) {
10 unlink($dump);
11}
12rmdir("./tests/dump_results/");
13?>
14--POST--
15post_a=data_post_a&post_b=data_post_b
16--GET--
17get_a=data_get_a&get_b=data_get_b
18--COOKIE--
19cookie_a=data_cookie_a&cookie_b=data_cookie_b
20--INI--
21sp.configuration_file={PWD}/config/dump_request.ini
22--FILE--
23<?php
24mkdir("./dump_results/");
25echo "1\n";
26echo system("echo 1337;");
27$filename = glob('./dump_results/*.dump')[0];
28$res = file($filename);
29if ($res[1] != "GET:get_a=data_get_a&get_b=data_get_b\n") {
30 echo "1\n";
31} elseif ($res[2] != "POST:post_a=data_post_a&post_b=data_post_b\n") {
32 echo "2\n";
33} elseif ($res[3] != "COOKIE:cookie_a=data_cookie_a&cookie_b=data_cookie_b\n") {
34 echo "3\n";
35}
36?>
37--EXPECTF--
381
39[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 0000000..b866f70
--- /dev/null
+++ b/src/tests/dump_request_invalid_folder.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Dump request - invalid folder.
3--SKIPIF--
4<?php
5if (!extension_loaded("snuffleupagus")) { print "skip"; }
6?>
7--POST--
8post_a=data_post_a&post_b=data_post_b
9--GET--
10get_a=data_get_a&get_b=data_get_b
11--COOKIE--
12cookie_a=data_cookie_a&cookie_b=data_cookie_b
13--INI--
14sp.configuration_file={PWD}/config/dump_request_invalid_folder.ini
15--FILE--
16<?php
17echo "1\n";
18echo system("echo 1337;");
19echo "2\n";
20?>
21--EXPECTF--
221
23[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.
24[snuffleupagus][0.0.0.0][request_logging][error] Unable to open /root/NON_EXISTENT/FOLDER/PLEASE/sp_dump_%a_0.0.0.0.dump
252 \ 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 0000000..81eb71c
--- /dev/null
+++ b/src/tests/dump_request_too_big.phpt
@@ -0,0 +1,42 @@
1--TEST--
2Dump request -- to big, so it's truncated.
3--SKIPIF--
4<?php
5if (!extension_loaded("snuffleupagus")) {
6 print "skip";
7}
8
9foreach (glob("./tests/dump_results/*.dump") as $dump) {
10 unlink($dump);
11}
12rmdir("./tests/dump_results/");
13?>
14--POST--
15post_a=data_post_a&post_b=data_post_b&post_c=c
16--GET--
17get_a=data_get_a&get_b=data_get_b&get_c=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaBBBB
18--COOKIE--
19cookie_a=data_cookie_a&cookie_b=data_cookie_b&data_cookie_c=cookie_c
20--ENV--
21return <<<END
22REMOTE_ADDR=127.0.0.1
23END;
24--INI--
25sp.configuration_file={PWD}/config/dump_request.ini
26--FILE--
27<?php
28echo "1\n";
29echo system("echo 1337;");
30$filename = glob('./dump_results/*.dump')[0];
31$res = file($filename);
32if ($res[1] != "GET:get_a=data_get_a&get_b=data_get_b&get_c=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n") {
33 echo "1\n";
34} elseif ($res[2] != "POST:post_a=data_post_a&post_b=data_post_b&post_c=c\n") {
35 echo "2\n";
36} elseif ($res[3] != "COOKIE:cookie_a=data_cookie_a&cookie_b=data_cookie_b&data_cookie_c=cookie_c\n") {
37 echo "3\n";
38}
39?>
40--EXPECTF--
411
42[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 0000000..411c817
--- /dev/null
+++ b/src/tests/empty_conf.phpt
@@ -0,0 +1,8 @@
1--TEST--
2Empty configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/empty_conf.ini
7--FILE--
8--EXPECT--
diff --git a/src/tests/encrypt_cookies.phpt b/src/tests/encrypt_cookies.phpt
new file mode 100644
index 0000000..f8bf64f
--- /dev/null
+++ b/src/tests/encrypt_cookies.phpt
@@ -0,0 +1,22 @@
1--TEST--
2Cookie decryption in ipv4
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_encrypted_cookies.ini
7--COOKIE--
8super_cookie=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEmXkk3H0xheoOMxoWPEDw1Zd8NAmD9KbB2DSjQ=%3d;awful_cookie=awful_cookie_value;
9--ENV--
10return <<<EOF
11REMOTE_ADDR=127.0.0.1
12HTTP_USER_AGENT=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/59.0.3071.109 Chrome/59.0.3071.109 Safari/537.36
13EOF;
14--FILE--
15<?php var_dump($_COOKIE); ?>
16--EXPECT--
17array(2) {
18 ["super_cookie"]=>
19 string(11) "super_value"
20 ["awful_cookie"]=>
21 string(18) "awful_cookie_value"
22}
diff --git a/src/tests/encrypt_cookies2.phpt b/src/tests/encrypt_cookies2.phpt
new file mode 100644
index 0000000..be4c990
--- /dev/null
+++ b/src/tests/encrypt_cookies2.phpt
@@ -0,0 +1,23 @@
1--TEST--
2Cookie encryption in ipv4
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_encrypted_cookies.ini
7--COOKIE--
8--ENV--
9return <<<EOF
10REMOTE_ADDR=127.0.0.1
11HTTP_USER_AGENT=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/59.0.3071.109 Chrome/59.0.3071.109 Safari/537.36
12HTTPS=1
13EOF;
14--FILE--
15<?php
16setcookie("super_cookie", "super_value");
17setcookie("awful_cookie", "awful_value");
18setcookie("nice_cookie", "nice_value", 1, "1", "1", true, true);
19var_dump($_COOKIE);
20?>
21--EXPECT--
22array(0) {
23}
diff --git a/src/tests/encrypt_cookies3.phpt b/src/tests/encrypt_cookies3.phpt
new file mode 100644
index 0000000..c85c5dc
--- /dev/null
+++ b/src/tests/encrypt_cookies3.phpt
@@ -0,0 +1,23 @@
1--TEST--
2Cookie decryption with ipv6
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_encrypted_cookies.ini
7--COOKIE--
8super_cookie=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJNTUge7MpiVNi4q3DqstbcumllXBir0CbIQiDI%3D;awful_cookie=awful_cookie_value;
9--ENV--
10return <<<EOF
11REMOTE_ADDR=2001:0db8:0000:0000:0000:fe00:0042:8329
12HTTP_USER_AGENT=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/59.0.3071.109 Chrome/59.0.3071.109 Safari/537.36
13HTTPS=1
14EOF;
15--FILE--
16<?php var_dump($_COOKIE); ?>
17--EXPECT--
18array(2) {
19 ["super_cookie"]=>
20 string(11) "super_value"
21 ["awful_cookie"]=>
22 string(18) "awful_cookie_value"
23}
diff --git a/src/tests/encrypt_cookies4.phpt b/src/tests/encrypt_cookies4.phpt
new file mode 100644
index 0000000..14d737a
--- /dev/null
+++ b/src/tests/encrypt_cookies4.phpt
@@ -0,0 +1,23 @@
1--TEST--
2Cookie encryption in ipv6
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_encrypted_cookies.ini
7--COOKIE--
8--ENV--
9return <<<EOF
10REMOTE_ADDR=2001:0db8:0000:0000:0000:fe00:0042:8329
11HTTP_USER_AGENT=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/59.0.3071.109 Chrome/59.0.3071.109 Safari/537.36
12HTTPS=1
13EOF;
14--FILE--
15<?php
16setcookie("super_cookie", "super_value");
17setcookie("awful_cookie", "awful_value");
18setcookie("nice_cookie", "nice_value", 1, "1", "1", true, true);
19var_dump($_COOKIE);
20?>
21--EXPECT--
22array(0) {
23}
diff --git a/src/tests/encrypt_cookies_invalid_decryption.phpt b/src/tests/encrypt_cookies_invalid_decryption.phpt
new file mode 100644
index 0000000..a5187c1
--- /dev/null
+++ b/src/tests/encrypt_cookies_invalid_decryption.phpt
@@ -0,0 +1,23 @@
1--TEST--
2Cookie encryption
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_encrypted_cookies.ini
7display_errors=1
8display_startup_errors=1
9error_reporting=E_ALL
10--COOKIE--
11super_cookie=jWjORGsgZyqzk3WA63XZBmUoSknXWnXDfAAAAAAAAAAAAAAAAAAAAAA7LiMDfkpP94jDnMVH%2Fm41GeL0Y00q3mbOFYz%2FS9mQGySu;awful_cookie=awful_cookie_value;
12--ENV--
13return <<<EOF
14REMOTE_ADDR=127.0.0.1
15EOF;
16--FILE--
17<?php var_dump($_COOKIE); ?>
18--EXPECT--
19
20array(1) {
21 ["awful_cookie"]=>
22 string(18) "awful_cookie_value"
23}
diff --git a/src/tests/encrypt_cookies_invalid_decryption2.phpt b/src/tests/encrypt_cookies_invalid_decryption2.phpt
new file mode 100644
index 0000000..f18cf6d
--- /dev/null
+++ b/src/tests/encrypt_cookies_invalid_decryption2.phpt
@@ -0,0 +1,23 @@
1--TEST--
2Cookie encryption
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_encrypted_cookies.ini
7display_errors=1
8display_startup_errors=1
9error_reporting=E_ALL
10--COOKIE--
11super_cookie=1337;awful_cookie=awful_cookie_value;
12--ENV--
13return <<<EOF
14REMOTE_ADDR=127.0.0.1
15EOF;
16--FILE--
17<?php var_dump($_COOKIE); ?>
18--EXPECT--
19
20array(1) {
21 ["awful_cookie"]=>
22 string(18) "awful_cookie_value"
23}
diff --git a/src/tests/encrypt_cookies_invalid_decryption3.phpt b/src/tests/encrypt_cookies_invalid_decryption3.phpt
new file mode 100644
index 0000000..f4afc32
--- /dev/null
+++ b/src/tests/encrypt_cookies_invalid_decryption3.phpt
@@ -0,0 +1,21 @@
1--TEST--
2Cookie encryption
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_encrypted_cookies.ini
7--COOKIE--
8super_cookie=;awful_cookie=awful_cookie_value;
9--ENV--
10return <<<EOF
11REMOTE_ADDR=127.0.0.1
12EOF;
13--FILE--
14<?php var_dump($_COOKIE); ?>
15--EXPECT--
16array(2) {
17 ["super_cookie"]=>
18 string(0) ""
19 ["awful_cookie"]=>
20 string(18) "awful_cookie_value"
21}
diff --git a/src/tests/encryption_key_only.phpt b/src/tests/encryption_key_only.phpt
new file mode 100644
index 0000000..bf5edb5
--- /dev/null
+++ b/src/tests/encryption_key_only.phpt
@@ -0,0 +1,13 @@
1--TEST--
2Encryption key only
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/encryption_key_only.ini
7--FILE--
8<?php
9echo 1337;
10?>
11--EXPECT--
121337
13
diff --git a/src/tests/example_configuration.phpt b/src/tests/example_configuration.phpt
new file mode 100644
index 0000000..0bbf59c
--- /dev/null
+++ b/src/tests/example_configuration.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Shipped configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/../../config/examples.ini
7--FILE--
8<?php
9system("echo 0");
10?>
11--EXPECTF--
120
diff --git a/src/tests/global_strict.phpt b/src/tests/global_strict.phpt
new file mode 100644
index 0000000..e06721c
--- /dev/null
+++ b/src/tests/global_strict.phpt
@@ -0,0 +1,16 @@
1--TEST--
2Global strict mode
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/global_strict.ini
7--FILE--
8<?php
9strcmp("pouet", []);
10?>
11--EXPECTF--
12Fatal error: Uncaught TypeError: strcmp() expects parameter 2 to be string, array given in %a/global_strict.php:2
13Stack trace:
14#0 %a/global_strict.php(2): strcmp('pouet', Array)
15#1 {main}
16 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 0000000..ca3ddfa
--- /dev/null
+++ b/src/tests/global_strict_disabled.phpt
@@ -0,0 +1,14 @@
1--TEST--
2Global strict mode
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/global_strict_disabled.ini
7--FILE--
8<?php
9strcmp("pouet", []);
10echo 1337;
11?>
12--EXPECTF--
13Warning: strcmp() expects parameter 2 to be string, array given in %a/global_strict_disabled.php on line 2
141337
diff --git a/src/tests/harden_mt_rand.phpt b/src/tests/harden_mt_rand.phpt
new file mode 100644
index 0000000..8887613
--- /dev/null
+++ b/src/tests/harden_mt_rand.phpt
@@ -0,0 +1,22 @@
1--TEST--
2Harden mt_rand
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/harden_rand.ini
7--FILE--
8<?php
9mt_srand(0);
10$a = mt_rand(0,100)."\n";
11$b = mt_rand(0,100)."\n";
12mt_srand(0);
13$c = mt_rand(0,100)."\n";
14$d = mt_rand(0,100)."\n";
15
16if ($a == $c && $b == $d)
17 echo 'lose';
18else
19 echo 'win';
20?>
21--EXPECT--
22win
diff --git a/src/tests/harden_rand.phpt b/src/tests/harden_rand.phpt
new file mode 100644
index 0000000..391bccc
--- /dev/null
+++ b/src/tests/harden_rand.phpt
@@ -0,0 +1,24 @@
1--TEST--
2Harden rand
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/harden_rand.ini
7--FILE--
8<?php
9srand(0);
10$a = rand(0,100)."\n";
11$b = rand(0,100)."\n";
12srand(0);
13$c = rand(0,100)."\n";
14$d = rand(0,100)."\n";
15
16rand(100,0)."\n";
17
18if ($a == $c && $b == $d)
19 echo 'fail';
20else
21 echo 'win';
22?>
23--EXPECT--
24win
diff --git a/src/tests/harden_rand_noargs.phpt b/src/tests/harden_rand_noargs.phpt
new file mode 100644
index 0000000..643a453
--- /dev/null
+++ b/src/tests/harden_rand_noargs.phpt
@@ -0,0 +1,62 @@
1--TEST--
2Harden rand without any arguments
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/harden_rand.ini
7We should fix this
8--FILE--
9<?php
10rand();
11mt_rand();
12
13rand(1);
14mt_rand(1);
15
16rand(1, 2);
17mt_rand(1, 2);
18
19rand(2, 1);
20mt_rand(2, 1);
21
22rand(2, 1, 0);
23mt_rand(2, 1, 0);
24
25rand("test", 1);
26mt_rand("test", 1);
27
28rand(1, "test");
29mt_rand(1, "test");
30
31rand(1, 2, "test");
32mt_rand(1, 2, "test");
33
34echo "Everything is fine\n";
35echo "Absolutely everything\n";
36echo 'Even with single quotes';
37?>
38--EXPECTF--
39Warning: rand() expects exactly 2 parameters, 1 given in %s/tests/harden_rand_noargs.php on line %d
40
41Warning: mt_rand() expects exactly 2 parameters, 1 given in %s/tests/harden_rand_noargs.php on line %d
42
43Warning: mt_rand(): max(1) is smaller than min(2) in %s/tests/harden_rand_noargs.php on line %d
44
45Warning: rand() expects exactly 2 parameters, 3 given in %s/tests/harden_rand_noargs.php on line %d
46
47Warning: mt_rand() expects exactly 2 parameters, 3 given in %s/tests/harden_rand_noargs.php on line %d
48
49Warning: rand() expects parameter 1 to be integer, string given in %s/tests/harden_rand_noargs.php on line %d
50
51Warning: mt_rand() expects parameter 1 to be integer, string given in %s/tests/harden_rand_noargs.php on line %d
52
53Warning: rand() expects parameter 2 to be integer, string given in %s/tests/harden_rand_noargs.php on line %d
54
55Warning: mt_rand() expects parameter 2 to be integer, string given in %s/tests/harden_rand_noargs.php on line %d
56
57Warning: rand() expects exactly 2 parameters, 3 given in %s/tests/harden_rand_noargs.php on line %d
58
59Warning: mt_rand() expects exactly 2 parameters, 3 given in %s/tests/harden_rand_noargs.php on line %d
60Everything is fine
61Absolutely everything
62Even with single quotes
diff --git a/src/tests/inexistent_conf_file.phpt b/src/tests/inexistent_conf_file.phpt
new file mode 100644
index 0000000..c7c3fcd
--- /dev/null
+++ b/src/tests/inexistent_conf_file.phpt
@@ -0,0 +1,10 @@
1--TEST--
2Check for snuffleupagus presence
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/unexistent_configuration_file.ini
7--FILE--
8<?php ?>
9--EXPECTF--
10[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 0000000..25e2e17
--- /dev/null
+++ b/src/tests/loading.phpt
@@ -0,0 +1,10 @@
1--TEST--
2Check for snuffleupagus presence
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--FILE--
6<?php
7echo "snuffleupagus extension is available";
8?>
9--EXPECT--
10snuffleupagus extension is available
diff --git a/src/tests/noncore_function_hooking.phpt b/src/tests/noncore_function_hooking.phpt
new file mode 100644
index 0000000..106123c
--- /dev/null
+++ b/src/tests/noncore_function_hooking.phpt
@@ -0,0 +1,15 @@
1--TEST--
2Hooking of user-defined functions
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_noncore_function_hooking.ini
7--FILE--
8<?php
9function custom_fun($a) {
10 echo $a;
11}
12custom_fun("hello");
13?>
14--EXPECTF--
15[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 0000000..35ed0ed
--- /dev/null
+++ b/src/tests/phpinfo_presence.phpt
@@ -0,0 +1,19 @@
1--TEST--
2Unserialize fail
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_serialize.ini
7--FILE--
8<?php
9ob_start () ;
10phpinfo () ;
11$pinfo = ob_get_contents () ;
12ob_end_clean () ;
13if (strstr($pinfo, "snuffleupagus") !== FALSE)
14 echo 1;
15else
16 echo 2;
17?>
18--EXPECT--
191
diff --git a/src/tests/serialize.phpt b/src/tests/serialize.phpt
new file mode 100644
index 0000000..e93dbaf
--- /dev/null
+++ b/src/tests/serialize.phpt
@@ -0,0 +1,13 @@
1--TEST--
2Test serialize hmac
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_serialize.ini
7--FILE--
8<?php
9echo serialize("a");
10?>
11--EXPECT--
12s:1:"a";650609b417904d0d9bbf1fc44a975d13ecdf6b02b715c1a06271fb3b673f25b1
13
diff --git a/src/tests/setcookie.phpt b/src/tests/setcookie.phpt
new file mode 100644
index 0000000..ba1d1c1
--- /dev/null
+++ b/src/tests/setcookie.phpt
@@ -0,0 +1,35 @@
1--TEST--
2Set cookies.
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_encrypted_cookies.ini
7--COOKIE--
8--ENV--
9return <<<EOF
10REMOTE_ADDR=127.0.0.1
11HTTP_USER_AGENT=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/59.0.3071.109 Chrome/59.0.3071.109 Safari/537.36
12HTTPS=1
13EOF;
14--FILE--
15<?php
16setcookie("name");
17setcookie("super_cookie");
18setcookie("name", "value");
19setcookie("name", "value1", 1);
20setcookie("name", "value2", 0);
21setcookie("name", "value", 1, "/super/path");
22setcookie("name", "value", 1, "/super/path", "super_domain");
23setcookie("name", "value", 1, "/super/path", "super_domain1", true);
24setcookie("name", "value", 1, "/super/path", "super_domain2", false);
25setcookie("name", "value", 1, "/super/path", "super_domain1", true, true);
26setcookie("name", "value", 1, "/super/path", "super_domain2", true, false);
27setcookie("name", "value", 1, "/super/path", "super_domain2", true, false, 1337);
28setcookie();
29echo '1337';
30?>
31--EXPECTF--
32Warning: setcookie() expects at most 7 parameters, 8 given in %a/setcookie.php on line %d
33
34Warning: setcookie() expects at least 1 parameter, 0 given in %a/setcookie.php on line %d
351337
diff --git a/src/tests/shipped_configuration.phpt b/src/tests/shipped_configuration.phpt
new file mode 100644
index 0000000..c060a85
--- /dev/null
+++ b/src/tests/shipped_configuration.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Shipped configuration
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/../../config/default.ini
7--FILE--
8<?php
9system("echo 0");
10?>
11--EXPECTF--
120
diff --git a/src/tests/unserialize.phpt b/src/tests/unserialize.phpt
new file mode 100644
index 0000000..b1db915
--- /dev/null
+++ b/src/tests/unserialize.phpt
@@ -0,0 +1,13 @@
1--TEST--
2Unserialize ok
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_serialize.ini
7--FILE--
8<?php
9$a=serialize("a");
10var_dump(unserialize($a));
11?>
12--EXPECT--
13string(1) "a"
diff --git a/src/tests/unserialize_fail.phpt b/src/tests/unserialize_fail.phpt
new file mode 100644
index 0000000..5c0bb80
--- /dev/null
+++ b/src/tests/unserialize_fail.phpt
@@ -0,0 +1,23 @@
1--TEST--
2Unserialize fail
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_serialize.ini
7--FILE--
8<?php
9var_dump(unserialize('s:1:"a";'));
10var_dump(unserialize('s:1:"a";alyualskdufyhalkdjsfhalkjdhflaksjdfhlkasdhflkahdawkuerylksjdfhlkssjgdflaksjdhflkasjdf'));
11var_dump(unserialize('s:1:"a";dslfjklfjfkjfdjffjfjads'));
12var_dump(unserialize(1,2,3,4));
13?>
14--EXPECTF--
15[snuffleupagus][0.0.0.0][unserialize][drop] The serialized object is too small.
16bool(false)
17[snuffleupagus][0.0.0.0][unserialize][drop] Invalid HMAC for s:1:"a";alyualskdufyhalkdjsfh
18NULL
19[snuffleupagus][0.0.0.0][unserialize][drop] The serialized object is too small.
20bool(false)
21
22Warning: unserialize() expects at most 2 parameters, 4 given in %a/tests/unserialize_fail.php on line %d
23bool(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 0000000..8ebf64d
--- /dev/null
+++ b/src/tests/unserialize_sim.phpt
@@ -0,0 +1,17 @@
1--TEST--
2Unserialize ok
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_serialize_sim.ini
7--FILE--
8<?php
9$a=serialize("a");
10echo $a;
11var_dump(unserialize($a));
12var_dump(unserialize('s:1:"a";alyualskdufyhalkdjsfhalkjdhflaksjdfhlkasdhflkahdawkuerylksjdfhlkssjgdflaksjdh1337sjdf'));
13?>
14--EXPECT--
15s:1:"a";650609b417904d0d9bbf1fc44a975d13ecdf6b02b715c1a06271fb3b673f25b1string(1) "a"
16[snuffleupagus][0.0.0.0][unserialize][notice] Invalid HMAC for s:1:"a";alyualskdufyhalkdjsfh
17string(1) "a"
diff --git a/src/tests/upload_validation.phpt b/src/tests/upload_validation.phpt
new file mode 100644
index 0000000..c802c16
--- /dev/null
+++ b/src/tests/upload_validation.phpt
@@ -0,0 +1,16 @@
1--TEST--
2Upload a file, validation ok, no simulation
3--INI--
4file_uploads=1
5sp.configuration_file={PWD}/config/upload_validation.ini
6--POST_RAW--
7Content-Type: multipart/form-data; boundary=blabla
8--blabla
9Content-Disposition: form-data; name="test"; filename="test.php"
10--blabla--
11--FILE--
12<?php
13echo 1;
14?>
15--EXPECTF--
161
diff --git a/src/tests/upload_validation_invalid.phpt b/src/tests/upload_validation_invalid.phpt
new file mode 100644
index 0000000..f8c993b
--- /dev/null
+++ b/src/tests/upload_validation_invalid.phpt
@@ -0,0 +1,17 @@
1--TEST--
2Upload a file, invalid validation script
3--INI--
4file_uploads=1
5sp.configuration_file={PWD}/config/upload_validation_invalid.ini
6--POST_RAW--
7Content-Type: multipart/form-data; boundary=blabla
8--blabla
9Content-Disposition: form-data; name="test"; filename="test.php"
10--blabla--
11--FILE--
12<?php
13echo 1;
14?>
15--EXPECTF--
16[snuffleupagus][0.0.0.0][upload_validation][error] Could not call './tests/data/upload_invalid.sh' : Exec format error
17[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 0000000..cf4057a
--- /dev/null
+++ b/src/tests/upload_validation_ko.phpt
@@ -0,0 +1,14 @@
1--TEST--
2Upload a file, validation ko, no simulation
3--INI--
4file_uploads=1
5sp.configuration_file={PWD}/config/upload_validation_ko.ini
6output_buffering=off
7--POST_RAW--
8Content-Type: multipart/form-data; boundary=blabla
9--blabla
10Content-Disposition: form-data; name="test"; filename="test.php"
11--blabla--
12--FILE--
13--EXPECTF--
14[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 0000000..90a58da
--- /dev/null
+++ b/src/tests/upload_validation_no_exec.phpt
@@ -0,0 +1,32 @@
1--TEST--
2Upload a file, validation script not executable
3--INI--
4file_uploads=1
5sp.configuration_file={PWD}/config/upload_validation_non_exec.ini
6output_buffering=off
7--POST_RAW--
8Content-Type: multipart/form-data; boundary=blabla
9--blabla
10Content-Disposition: form-data; name="test"; filename="test.php"
11--blabla--
12--FILE--
13<?php
14var_dump($_FILES);
15echo "\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
16?>
17--EXPECTF--
18array(1) {
19 ["test"]=>
20 array(5) {
21 ["name"]=>
22 string(8) "test.php"
23 ["type"]=>
24 string(0) ""
25 ["tmp_name"]=>
26 string(0) ""
27 ["error"]=>
28 int(3)
29 ["size"]=>
30 int(0)
31 }
32}
diff --git a/src/tests/upload_validation_nocrash.phpt b/src/tests/upload_validation_nocrash.phpt
new file mode 100644
index 0000000..6fa50d0
--- /dev/null
+++ b/src/tests/upload_validation_nocrash.phpt
@@ -0,0 +1,12 @@
1--TEST--
2Upload validation isn't crashing
3--INI--
4file_uploads=1
5sp.configuration_file={PWD}/config/upload_validation_ok.ini
6output_buffering=off
7--FILE--
8<?php
9echo 1;
10?>
11--EXPECTF--
121
diff --git a/src/tests/upload_validation_ok.phpt b/src/tests/upload_validation_ok.phpt
new file mode 100644
index 0000000..f9b5015
--- /dev/null
+++ b/src/tests/upload_validation_ok.phpt
@@ -0,0 +1,17 @@
1--TEST--
2Upload a file, validation ok, no simulation
3--INI--
4file_uploads=1
5sp.configuration_file={PWD}/config/upload_validation_ok.ini
6output_buffering=off
7--POST_RAW--
8Content-Type: multipart/form-data; boundary=blabla
9--blabla
10Content-Disposition: form-data; name="test"; filename="test.php"
11--blabla--
12--FILE--
13<?php
14echo 1;
15?>
16--EXPECTF--
171
diff --git a/src/tweetnacl.c b/src/tweetnacl.c
new file mode 100644
index 0000000..937e879
--- /dev/null
+++ b/src/tweetnacl.c
@@ -0,0 +1,842 @@
1#include "tweetnacl.h"
2#define FOR(i,n) for (i = 0;i < n;++i)
3#define sv static void
4
5typedef unsigned char u8;
6typedef unsigned long u32;
7typedef unsigned long long u64;
8typedef long long i64;
9typedef i64 gf[16];
10
11
12/* it's really stupid that there isn't a syscall for this */
13
14static int fd = -1;
15
16void randombytes(unsigned char *x,unsigned long long xlen)
17{
18 int i;
19
20 if (fd == -1) {
21 for (;;) {
22 fd = open("/dev/urandom",O_RDONLY);
23 if (fd != -1) break;
24 sleep(1);
25 }
26 }
27
28 while (xlen > 0) {
29 if (xlen < 1048576) i = xlen; else i = 1048576;
30
31 i = read(fd,x,i);
32 if (i < 1) {
33 sleep(1);
34 continue;
35 }
36
37 x += i;
38 xlen -= i;
39 }
40}
41
42
43static const u8
44 _0[16],
45 _9[32] = {9};
46static const gf
47 gf0,
48 gf1 = {1},
49 _121665 = {0xDB41,1},
50 D = {0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203},
51 D2 = {0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406},
52 X = {0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169},
53 Y = {0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666},
54 I = {0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83};
55
56static u32 L32(u32 x,int c) { return (x << c) | ((x&0xffffffff) >> (32 - c)); }
57
58static u32 ld32(const u8 *x)
59{
60 u32 u = x[3];
61 u = (u<<8)|x[2];
62 u = (u<<8)|x[1];
63 return (u<<8)|x[0];
64}
65
66static u64 dl64(const u8 *x)
67{
68 u64 i,u=0;
69 FOR(i,8) u=(u<<8)|x[i];
70 return u;
71}
72
73sv st32(u8 *x,u32 u)
74{
75 int i;
76 FOR(i,4) { x[i] = u; u >>= 8; }
77}
78
79sv ts64(u8 *x,u64 u)
80{
81 int i;
82 for (i = 7;i >= 0;--i) { x[i] = u; u >>= 8; }
83}
84
85static int vn(const u8 *x,const u8 *y,int n)
86{
87 int i = 0;
88 u32 d = 0;
89 FOR(i,n) d |= x[i]^y[i];
90 return (1 & ((d - 1) >> 8)) - 1;
91}
92
93int crypto_verify_16(const u8 *x,const u8 *y)
94{
95 return vn(x,y,16);
96}
97
98int crypto_verify_32(const u8 *x,const u8 *y)
99{
100 return vn(x,y,32);
101}
102
103sv core(u8 *out,const u8 *in,const u8 *k,const u8 *c,int h)
104{
105 u32 w[16],x[16],y[16],t[4];
106 int i,j,m;
107
108 FOR(i,4) {
109 x[5*i] = ld32(c+4*i);
110 x[1+i] = ld32(k+4*i);
111 x[6+i] = ld32(in+4*i);
112 x[11+i] = ld32(k+16+4*i);
113 }
114
115 FOR(i,16) y[i] = x[i];
116
117 FOR(i,20) {
118 FOR(j,4) {
119 FOR(m,4) t[m] = x[(5*j+4*m)%16];
120 t[1] ^= L32(t[0]+t[3], 7);
121 t[2] ^= L32(t[1]+t[0], 9);
122 t[3] ^= L32(t[2]+t[1],13);
123 t[0] ^= L32(t[3]+t[2],18);
124 FOR(m,4) w[4*j+(j+m)%4] = t[m];
125 }
126 FOR(m,16) x[m] = w[m];
127 }
128
129 if (h) {
130 FOR(i,16) x[i] += y[i];
131 FOR(i,4) {
132 x[5*i] -= ld32(c+4*i);
133 x[6+i] -= ld32(in+4*i);
134 }
135 FOR(i,4) {
136 st32(out+4*i,x[5*i]);
137 st32(out+16+4*i,x[6+i]);
138 }
139 } else
140 FOR(i,16) st32(out + 4 * i,x[i] + y[i]);
141}
142
143int crypto_core_salsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c)
144{
145 core(out,in,k,c,0);
146 return 0;
147}
148
149int crypto_core_hsalsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c)
150{
151 core(out,in,k,c,1);
152 return 0;
153}
154
155static const u8 sigma[16] = "expand 32-byte k";
156
157int crypto_stream_salsa20_xor(u8 *c,const u8 *m,u64 b,const u8 *n,const u8 *k)
158{
159 u8 z[16],x[64];
160 u32 u,i;
161 if (!b) return 0;
162 FOR(i,16) z[i] = 0;
163 FOR(i,8) z[i] = n[i];
164 while (b >= 64) {
165 crypto_core_salsa20(x,z,k,sigma);
166 FOR(i,64) c[i] = (m?m[i]:0) ^ x[i];
167 u = 1;
168 for (i = 8;i < 16;++i) {
169 u += (u32) z[i];
170 z[i] = u;
171 u >>= 8;
172 }
173 b -= 64;
174 c += 64;
175 if (m) m += 64;
176 }
177 if (b) {
178 crypto_core_salsa20(x,z,k,sigma);
179 FOR(i,b) c[i] = (m?m[i]:0) ^ x[i];
180 }
181 return 0;
182}
183
184int crypto_stream_salsa20(u8 *c,u64 d,const u8 *n,const u8 *k)
185{
186 return crypto_stream_salsa20_xor(c,0,d,n,k);
187}
188
189int crypto_stream(u8 *c,u64 d,const u8 *n,const u8 *k)
190{
191 u8 s[32];
192 crypto_core_hsalsa20(s,n,k,sigma);
193 return crypto_stream_salsa20(c,d,n+16,s);
194}
195
196int crypto_stream_xor(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k)
197{
198 u8 s[32];
199 crypto_core_hsalsa20(s,n,k,sigma);
200 return crypto_stream_salsa20_xor(c,m,d,n+16,s);
201}
202
203sv add1305(u32 *h,const u32 *c)
204{
205 u32 j,u = 0;
206 FOR(j,17) {
207 u += h[j] + c[j];
208 h[j] = u & 255;
209 u >>= 8;
210 }
211}
212
213static const u32 minusp[17] = {
214 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252
215} ;
216
217int crypto_onetimeauth(u8 *out,const u8 *m,u64 n,const u8 *k)
218{
219 u32 s,i,j,u,x[17],r[17],h[17],c[17],g[17];
220
221 FOR(j,17) r[j]=h[j]=0;
222 FOR(j,16) r[j]=k[j];
223 r[3]&=15;
224 r[4]&=252;
225 r[7]&=15;
226 r[8]&=252;
227 r[11]&=15;
228 r[12]&=252;
229 r[15]&=15;
230
231 while (n > 0) {
232 FOR(j,17) c[j] = 0;
233 for (j = 0;(j < 16) && (j < n);++j) c[j] = m[j];
234 c[j] = 1;
235 m += j; n -= j;
236 add1305(h,c);
237 FOR(i,17) {
238 x[i] = 0;
239 FOR(j,17) x[i] += h[j] * ((j <= i) ? r[i - j] : 320 * r[i + 17 - j]);
240 }
241 FOR(i,17) h[i] = x[i];
242 u = 0;
243 FOR(j,16) {
244 u += h[j];
245 h[j] = u & 255;
246 u >>= 8;
247 }
248 u += h[16]; h[16] = u & 3;
249 u = 5 * (u >> 2);
250 FOR(j,16) {
251 u += h[j];
252 h[j] = u & 255;
253 u >>= 8;
254 }
255 u += h[16]; h[16] = u;
256 }
257
258 FOR(j,17) g[j] = h[j];
259 add1305(h,minusp);
260 s = -(h[16] >> 7);
261 FOR(j,17) h[j] ^= s & (g[j] ^ h[j]);
262
263 FOR(j,16) c[j] = k[j + 16];
264 c[16] = 0;
265 add1305(h,c);
266 FOR(j,16) out[j] = h[j];
267 return 0;
268}
269
270int crypto_onetimeauth_verify(const u8 *h,const u8 *m,u64 n,const u8 *k)
271{
272 u8 x[16];
273 crypto_onetimeauth(x,m,n,k);
274 return crypto_verify_16(h,x);
275}
276
277int crypto_secretbox(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k)
278{
279 int i;
280 if (d < 32) return -1;
281 crypto_stream_xor(c,m,d,n,k);
282 crypto_onetimeauth(c + 16,c + 32,d - 32,c);
283 FOR(i,16) c[i] = 0;
284 return 0;
285}
286
287int crypto_secretbox_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k)
288{
289 int i;
290 u8 x[32];
291 if (d < 32) return -1;
292 crypto_stream(x,32,n,k);
293 if (crypto_onetimeauth_verify(c + 16,c + 32,d - 32,x) != 0) return -1;
294 crypto_stream_xor(m,c,d,n,k);
295 FOR(i,32) m[i] = 0;
296 return 0;
297}
298
299sv set25519(gf r, const gf a)
300{
301 int i;
302 FOR(i,16) r[i]=a[i];
303}
304
305sv car25519(gf o)
306{
307 int i;
308 i64 c;
309 FOR(i,16) {
310 o[i]+=(1LL<<16);
311 c=o[i]>>16;
312 o[(i+1)*(i<15)]+=c-1+37*(c-1)*(i==15);
313 o[i]-=c<<16;
314 }
315}
316
317sv sel25519(gf p,gf q,int b)
318{
319 i64 t,i,c=~(b-1);
320 FOR(i,16) {
321 t= c&(p[i]^q[i]);
322 p[i]^=t;
323 q[i]^=t;
324 }
325}
326
327sv pack25519(u8 *o,const gf n)
328{
329 int i,j,b;
330 gf m,t;
331 FOR(i,16) t[i]=n[i];
332 car25519(t);
333 car25519(t);
334 car25519(t);
335 FOR(j,2) {
336 m[0]=t[0]-0xffed;
337 for(i=1;i<15;i++) {
338 m[i]=t[i]-0xffff-((m[i-1]>>16)&1);
339 m[i-1]&=0xffff;
340 }
341 m[15]=t[15]-0x7fff-((m[14]>>16)&1);
342 b=(m[15]>>16)&1;
343 m[14]&=0xffff;
344 sel25519(t,m,1-b);
345 }
346 FOR(i,16) {
347 o[2*i]=t[i]&0xff;
348 o[2*i+1]=t[i]>>8;
349 }
350}
351
352static int neq25519(const gf a, const gf b)
353{
354 u8 c[32],d[32];
355 pack25519(c,a);
356 pack25519(d,b);
357 return crypto_verify_32(c,d);
358}
359
360static u8 par25519(const gf a)
361{
362 u8 d[32];
363 pack25519(d,a);
364 return d[0]&1;
365}
366
367sv unpack25519(gf o, const u8 *n)
368{
369 int i;
370 FOR(i,16) o[i]=n[2*i]+((i64)n[2*i+1]<<8);
371 o[15]&=0x7fff;
372}
373
374sv A(gf o,const gf a,const gf b)
375{
376 int i;
377 FOR(i,16) o[i]=a[i]+b[i];
378}
379
380sv Z(gf o,const gf a,const gf b)
381{
382 int i;
383 FOR(i,16) o[i]=a[i]-b[i];
384}
385
386sv M(gf o,const gf a,const gf b)
387{
388 i64 i,j,t[31];
389 FOR(i,31) t[i]=0;
390 FOR(i,16) FOR(j,16) t[i+j]+=a[i]*b[j];
391 FOR(i,15) t[i]+=38*t[i+16];
392 FOR(i,16) o[i]=t[i];
393 car25519(o);
394 car25519(o);
395}
396
397sv S(gf o,const gf a)
398{
399 M(o,a,a);
400}
401
402sv inv25519(gf o,const gf i)
403{
404 gf c;
405 int a;
406 FOR(a,16) c[a]=i[a];
407 for(a=253;a>=0;a--) {
408 S(c,c);
409 if(a!=2&&a!=4) M(c,c,i);
410 }
411 FOR(a,16) o[a]=c[a];
412}
413
414sv pow2523(gf o,const gf i)
415{
416 gf c;
417 int a;
418 FOR(a,16) c[a]=i[a];
419 for(a=250;a>=0;a--) {
420 S(c,c);
421 if(a!=1) M(c,c,i);
422 }
423 FOR(a,16) o[a]=c[a];
424}
425
426int crypto_scalarmult(u8 *q,const u8 *n,const u8 *p)
427{
428 u8 z[32];
429 i64 x[80],r,i;
430 gf a,b,c,d,e,f;
431 FOR(i,31) z[i]=n[i];
432 z[31]=(n[31]&127)|64;
433 z[0]&=248;
434 unpack25519(x,p);
435 FOR(i,16) {
436 b[i]=x[i];
437 d[i]=a[i]=c[i]=0;
438 }
439 a[0]=d[0]=1;
440 for(i=254;i>=0;--i) {
441 r=(z[i>>3]>>(i&7))&1;
442 sel25519(a,b,r);
443 sel25519(c,d,r);
444 A(e,a,c);
445 Z(a,a,c);
446 A(c,b,d);
447 Z(b,b,d);
448 S(d,e);
449 S(f,a);
450 M(a,c,a);
451 M(c,b,e);
452 A(e,a,c);
453 Z(a,a,c);
454 S(b,a);
455 Z(c,d,f);
456 M(a,c,_121665);
457 A(a,a,d);
458 M(c,c,a);
459 M(a,d,f);
460 M(d,b,x);
461 S(b,e);
462 sel25519(a,b,r);
463 sel25519(c,d,r);
464 }
465 FOR(i,16) {
466 x[i+16]=a[i];
467 x[i+32]=c[i];
468 x[i+48]=b[i];
469 x[i+64]=d[i];
470 }
471 inv25519(x+32,x+32);
472 M(x+16,x+16,x+32);
473 pack25519(q,x+16);
474 return 0;
475}
476
477int crypto_scalarmult_base(u8 *q,const u8 *n)
478{
479 return crypto_scalarmult(q,n,_9);
480}
481
482int crypto_box_keypair(u8 *y,u8 *x)
483{
484 randombytes(x,32);
485 return crypto_scalarmult_base(y,x);
486}
487
488int crypto_box_beforenm(u8 *k,const u8 *y,const u8 *x)
489{
490 u8 s[32];
491 crypto_scalarmult(s,x,y);
492 return crypto_core_hsalsa20(k,_0,s,sigma);
493}
494
495int crypto_box_afternm(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k)
496{
497 return crypto_secretbox(c,m,d,n,k);
498}
499
500int crypto_box_open_afternm(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k)
501{
502 return crypto_secretbox_open(m,c,d,n,k);
503}
504
505int crypto_box(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *y,const u8 *x)
506{
507 u8 k[32];
508 crypto_box_beforenm(k,y,x);
509 return crypto_box_afternm(c,m,d,n,k);
510}
511
512int crypto_box_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *y,const u8 *x)
513{
514 u8 k[32];
515 crypto_box_beforenm(k,y,x);
516 return crypto_box_open_afternm(m,c,d,n,k);
517}
518
519static u64 R(u64 x,int c) { return (x >> c) | (x << (64 - c)); }
520static u64 Ch(u64 x,u64 y,u64 z) { return (x & y) ^ (~x & z); }
521static u64 Maj(u64 x,u64 y,u64 z) { return (x & y) ^ (x & z) ^ (y & z); }
522static u64 Sigma0(u64 x) { return R(x,28) ^ R(x,34) ^ R(x,39); }
523static u64 Sigma1(u64 x) { return R(x,14) ^ R(x,18) ^ R(x,41); }
524static u64 sigma0(u64 x) { return R(x, 1) ^ R(x, 8) ^ (x >> 7); }
525static u64 sigma1(u64 x) { return R(x,19) ^ R(x,61) ^ (x >> 6); }
526
527static const u64 K[80] =
528{
529 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
530 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
531 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
532 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
533 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
534 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
535 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
536 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
537 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
538 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
539 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
540 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
541 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
542 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
543 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
544 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
545 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
546 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
547 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
548 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
549};
550
551int crypto_hashblocks(u8 *x,const u8 *m,u64 n)
552{
553 u64 z[8],b[8],a[8],w[16],t;
554 int i,j;
555
556 FOR(i,8) z[i] = a[i] = dl64(x + 8 * i);
557
558 while (n >= 128) {
559 FOR(i,16) w[i] = dl64(m + 8 * i);
560
561 FOR(i,80) {
562 FOR(j,8) b[j] = a[j];
563 t = a[7] + Sigma1(a[4]) + Ch(a[4],a[5],a[6]) + K[i] + w[i%16];
564 b[7] = t + Sigma0(a[0]) + Maj(a[0],a[1],a[2]);
565 b[3] += t;
566 FOR(j,8) a[(j+1)%8] = b[j];
567 if (i%16 == 15)
568 FOR(j,16)
569 w[j] += w[(j+9)%16] + sigma0(w[(j+1)%16]) + sigma1(w[(j+14)%16]);
570 }
571
572 FOR(i,8) { a[i] += z[i]; z[i] = a[i]; }
573
574 m += 128;
575 n -= 128;
576 }
577
578 FOR(i,8) ts64(x+8*i,z[i]);
579
580 return n;
581}
582
583static const u8 iv[64] = {
584 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08,
585 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b,
586 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b,
587 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1,
588 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1,
589 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f,
590 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b,
591 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79
592} ;
593
594int crypto_hash(u8 *out,const u8 *m,u64 n)
595{
596 u8 h[64],x[256];
597 u64 i,b = n;
598
599 FOR(i,64) h[i] = iv[i];
600
601 crypto_hashblocks(h,m,n);
602 m += n;
603 n &= 127;
604 m -= n;
605
606 FOR(i,256) x[i] = 0;
607 FOR(i,n) x[i] = m[i];
608 x[n] = 128;
609
610 n = 256-128*(n<112);
611 x[n-9] = b >> 61;
612 ts64(x+n-8,b<<3);
613 crypto_hashblocks(h,x,n);
614
615 FOR(i,64) out[i] = h[i];
616
617 return 0;
618}
619
620sv add(gf p[4],gf q[4])
621{
622 gf a,b,c,d,t,e,f,g,h;
623
624 Z(a, p[1], p[0]);
625 Z(t, q[1], q[0]);
626 M(a, a, t);
627 A(b, p[0], p[1]);
628 A(t, q[0], q[1]);
629 M(b, b, t);
630 M(c, p[3], q[3]);
631 M(c, c, D2);
632 M(d, p[2], q[2]);
633 A(d, d, d);
634 Z(e, b, a);
635 Z(f, d, c);
636 A(g, d, c);
637 A(h, b, a);
638
639 M(p[0], e, f);
640 M(p[1], h, g);
641 M(p[2], g, f);
642 M(p[3], e, h);
643}
644
645sv cswap(gf p[4],gf q[4],u8 b)
646{
647 int i;
648 FOR(i,4)
649 sel25519(p[i],q[i],b);
650}
651
652sv pack(u8 *r,gf p[4])
653{
654 gf tx, ty, zi;
655 inv25519(zi, p[2]);
656 M(tx, p[0], zi);
657 M(ty, p[1], zi);
658 pack25519(r, ty);
659 r[31] ^= par25519(tx) << 7;
660}
661
662sv scalarmult(gf p[4],gf q[4],const u8 *s)
663{
664 int i;
665 set25519(p[0],gf0);
666 set25519(p[1],gf1);
667 set25519(p[2],gf1);
668 set25519(p[3],gf0);
669 for (i = 255;i >= 0;--i) {
670 u8 b = (s[i/8]>>(i&7))&1;
671 cswap(p,q,b);
672 add(q,p);
673 add(p,p);
674 cswap(p,q,b);
675 }
676}
677
678sv scalarbase(gf p[4],const u8 *s)
679{
680 gf q[4];
681 set25519(q[0],X);
682 set25519(q[1],Y);
683 set25519(q[2],gf1);
684 M(q[3],X,Y);
685 scalarmult(p,q,s);
686}
687
688int crypto_sign_keypair(u8 *pk, u8 *sk)
689{
690 u8 d[64];
691 gf p[4];
692 int i;
693
694 randombytes(sk, 32);
695 crypto_hash(d, sk, 32);
696 d[0] &= 248;
697 d[31] &= 127;
698 d[31] |= 64;
699
700 scalarbase(p,d);
701 pack(pk,p);
702
703 FOR(i,32) sk[32 + i] = pk[i];
704 return 0;
705}
706
707static 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};
708
709sv modL(u8 *r,i64 x[64])
710{
711 i64 carry,i,j;
712 for (i = 63;i >= 32;--i) {
713 carry = 0;
714 for (j = i - 32;j < i - 12;++j) {
715 x[j] += carry - 16 * x[i] * L[j - (i - 32)];
716 carry = (x[j] + 128) >> 8;
717 x[j] -= carry << 8;
718 }
719 x[j] += carry;
720 x[i] = 0;
721 }
722 carry = 0;
723 FOR(j,32) {
724 x[j] += carry - (x[31] >> 4) * L[j];
725 carry = x[j] >> 8;
726 x[j] &= 255;
727 }
728 FOR(j,32) x[j] -= carry * L[j];
729 FOR(i,32) {
730 x[i+1] += x[i] >> 8;
731 r[i] = x[i] & 255;
732 }
733}
734
735sv reduce(u8 *r)
736{
737 i64 x[64],i;
738 FOR(i,64) x[i] = (u64) r[i];
739 FOR(i,64) r[i] = 0;
740 modL(r,x);
741}
742
743int crypto_sign(u8 *sm,u64 *smlen,const u8 *m,u64 n,const u8 *sk)
744{
745 u8 d[64],h[64],r[64];
746 u64 i;
747 i64 j,x[64];
748 gf p[4];
749
750 crypto_hash(d, sk, 32);
751 d[0] &= 248;
752 d[31] &= 127;
753 d[31] |= 64;
754
755 *smlen = n+64;
756 FOR(i,n) sm[64 + i] = m[i];
757 FOR(i,32) sm[32 + i] = d[32 + i];
758
759 crypto_hash(r, sm+32, n+32);
760 reduce(r);
761 scalarbase(p,r);
762 pack(sm,p);
763
764 FOR(i,32) sm[i+32] = sk[i+32];
765 crypto_hash(h,sm,n + 64);
766 reduce(h);
767
768 FOR(i,64) x[i] = 0;
769 FOR(i,32) x[i] = (u64) r[i];
770 FOR(i,32) FOR(j,32) x[i+j] += h[i] * (u64) d[j];
771 modL(sm + 32,x);
772
773 return 0;
774}
775
776static int unpackneg(gf r[4],const u8 p[32])
777{
778 gf t, chk, num, den, den2, den4, den6;
779 set25519(r[2],gf1);
780 unpack25519(r[1],p);
781 S(num,r[1]);
782 M(den,num,D);
783 Z(num,num,r[2]);
784 A(den,r[2],den);
785
786 S(den2,den);
787 S(den4,den2);
788 M(den6,den4,den2);
789 M(t,den6,num);
790 M(t,t,den);
791
792 pow2523(t,t);
793 M(t,t,num);
794 M(t,t,den);
795 M(t,t,den);
796 M(r[0],t,den);
797
798 S(chk,r[0]);
799 M(chk,chk,den);
800 if (neq25519(chk, num)) M(r[0],r[0],I);
801
802 S(chk,r[0]);
803 M(chk,chk,den);
804 if (neq25519(chk, num)) return -1;
805
806 if (par25519(r[0]) == (p[31]>>7)) Z(r[0],gf0,r[0]);
807
808 M(r[3],r[0],r[1]);
809 return 0;
810}
811
812int crypto_sign_open(u8 *m,u64 *mlen,const u8 *sm,u64 n,const u8 *pk)
813{
814 u64 i;
815 u8 t[32],h[64];
816 gf p[4],q[4];
817
818 *mlen = -1;
819 if (n < 64) return -1;
820
821 if (unpackneg(q,pk)) return -1;
822
823 FOR(i,n) m[i] = sm[i];
824 FOR(i,32) m[i+32] = pk[i];
825 crypto_hash(h,m,n);
826 reduce(h);
827 scalarmult(p,q,h);
828
829 scalarbase(q,sm + 32);
830 add(p,q);
831 pack(t,p);
832
833 n -= 64;
834 if (crypto_verify_32(sm, t)) {
835 FOR(i,n) m[i] = 0;
836 return -1;
837 }
838
839 FOR(i,n) m[i] = sm[i + 64];
840 *mlen = n;
841 return 0;
842}
diff --git a/src/tweetnacl.h b/src/tweetnacl.h
new file mode 100644
index 0000000..508876d
--- /dev/null
+++ b/src/tweetnacl.h
@@ -0,0 +1,277 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <fcntl.h>
4#include <unistd.h>
5
6#ifndef TWEETNACL_H
7#define TWEETNACL_H
8#define crypto_auth_PRIMITIVE "hmacsha512256"
9#define crypto_auth crypto_auth_hmacsha512256
10#define crypto_auth_verify crypto_auth_hmacsha512256_verify
11#define crypto_auth_BYTES crypto_auth_hmacsha512256_BYTES
12#define crypto_auth_KEYBYTES crypto_auth_hmacsha512256_KEYBYTES
13#define crypto_auth_IMPLEMENTATION crypto_auth_hmacsha512256_IMPLEMENTATION
14#define crypto_auth_VERSION crypto_auth_hmacsha512256_VERSION
15#define crypto_auth_hmacsha512256_tweet_BYTES 32
16#define crypto_auth_hmacsha512256_tweet_KEYBYTES 32
17extern int crypto_auth_hmacsha512256_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *);
18extern int crypto_auth_hmacsha512256_tweet_verify(const unsigned char *,const unsigned char *,unsigned long long,const unsigned char *);
19#define crypto_auth_hmacsha512256_tweet_VERSION "-"
20#define crypto_auth_hmacsha512256 crypto_auth_hmacsha512256_tweet
21#define crypto_auth_hmacsha512256_verify crypto_auth_hmacsha512256_tweet_verify
22#define crypto_auth_hmacsha512256_BYTES crypto_auth_hmacsha512256_tweet_BYTES
23#define crypto_auth_hmacsha512256_KEYBYTES crypto_auth_hmacsha512256_tweet_KEYBYTES
24#define crypto_auth_hmacsha512256_VERSION crypto_auth_hmacsha512256_tweet_VERSION
25#define crypto_auth_hmacsha512256_IMPLEMENTATION "crypto_auth/hmacsha512256/tweet"
26#define crypto_box_PRIMITIVE "curve25519xsalsa20poly1305"
27#define crypto_box crypto_box_curve25519xsalsa20poly1305
28#define crypto_box_open crypto_box_curve25519xsalsa20poly1305_open
29#define crypto_box_keypair crypto_box_curve25519xsalsa20poly1305_keypair
30#define crypto_box_beforenm crypto_box_curve25519xsalsa20poly1305_beforenm
31#define crypto_box_afternm crypto_box_curve25519xsalsa20poly1305_afternm
32#define crypto_box_open_afternm crypto_box_curve25519xsalsa20poly1305_open_afternm
33#define crypto_box_PUBLICKEYBYTES crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES
34#define crypto_box_SECRETKEYBYTES crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES
35#define crypto_box_BEFORENMBYTES crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES
36#define crypto_box_NONCEBYTES crypto_box_curve25519xsalsa20poly1305_NONCEBYTES
37#define crypto_box_ZEROBYTES crypto_box_curve25519xsalsa20poly1305_ZEROBYTES
38#define crypto_box_BOXZEROBYTES crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES
39#define crypto_box_IMPLEMENTATION crypto_box_curve25519xsalsa20poly1305_IMPLEMENTATION
40#define crypto_box_VERSION crypto_box_curve25519xsalsa20poly1305_VERSION
41#define crypto_box_curve25519xsalsa20poly1305_tweet_PUBLICKEYBYTES 32
42#define crypto_box_curve25519xsalsa20poly1305_tweet_SECRETKEYBYTES 32
43#define crypto_box_curve25519xsalsa20poly1305_tweet_BEFORENMBYTES 32
44#define crypto_box_curve25519xsalsa20poly1305_tweet_NONCEBYTES 24
45#define crypto_box_curve25519xsalsa20poly1305_tweet_ZEROBYTES 32
46#define crypto_box_curve25519xsalsa20poly1305_tweet_BOXZEROBYTES 16
47extern int crypto_box_curve25519xsalsa20poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *,const unsigned char *);
48extern int crypto_box_curve25519xsalsa20poly1305_tweet_open(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *,const unsigned char *);
49extern int crypto_box_curve25519xsalsa20poly1305_tweet_keypair(unsigned char *,unsigned char *);
50extern int crypto_box_curve25519xsalsa20poly1305_tweet_beforenm(unsigned char *,const unsigned char *,const unsigned char *);
51extern int crypto_box_curve25519xsalsa20poly1305_tweet_afternm(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *);
52extern int crypto_box_curve25519xsalsa20poly1305_tweet_open_afternm(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *);
53#define crypto_box_curve25519xsalsa20poly1305_tweet_VERSION "-"
54#define crypto_box_curve25519xsalsa20poly1305 crypto_box_curve25519xsalsa20poly1305_tweet
55#define crypto_box_curve25519xsalsa20poly1305_open crypto_box_curve25519xsalsa20poly1305_tweet_open
56#define crypto_box_curve25519xsalsa20poly1305_keypair crypto_box_curve25519xsalsa20poly1305_tweet_keypair
57#define crypto_box_curve25519xsalsa20poly1305_beforenm crypto_box_curve25519xsalsa20poly1305_tweet_beforenm
58#define crypto_box_curve25519xsalsa20poly1305_afternm crypto_box_curve25519xsalsa20poly1305_tweet_afternm
59#define crypto_box_curve25519xsalsa20poly1305_open_afternm crypto_box_curve25519xsalsa20poly1305_tweet_open_afternm
60#define crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES crypto_box_curve25519xsalsa20poly1305_tweet_PUBLICKEYBYTES
61#define crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES crypto_box_curve25519xsalsa20poly1305_tweet_SECRETKEYBYTES
62#define crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES crypto_box_curve25519xsalsa20poly1305_tweet_BEFORENMBYTES
63#define crypto_box_curve25519xsalsa20poly1305_NONCEBYTES crypto_box_curve25519xsalsa20poly1305_tweet_NONCEBYTES
64#define crypto_box_curve25519xsalsa20poly1305_ZEROBYTES crypto_box_curve25519xsalsa20poly1305_tweet_ZEROBYTES
65#define crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES crypto_box_curve25519xsalsa20poly1305_tweet_BOXZEROBYTES
66#define crypto_box_curve25519xsalsa20poly1305_VERSION crypto_box_curve25519xsalsa20poly1305_tweet_VERSION
67#define crypto_box_curve25519xsalsa20poly1305_IMPLEMENTATION "crypto_box/curve25519xsalsa20poly1305/tweet"
68#define crypto_core_PRIMITIVE "salsa20"
69#define crypto_core crypto_core_salsa20
70#define crypto_core_OUTPUTBYTES crypto_core_salsa20_OUTPUTBYTES
71#define crypto_core_INPUTBYTES crypto_core_salsa20_INPUTBYTES
72#define crypto_core_KEYBYTES crypto_core_salsa20_KEYBYTES
73#define crypto_core_CONSTBYTES crypto_core_salsa20_CONSTBYTES
74#define crypto_core_IMPLEMENTATION crypto_core_salsa20_IMPLEMENTATION
75#define crypto_core_VERSION crypto_core_salsa20_VERSION
76#define crypto_core_salsa20_tweet_OUTPUTBYTES 64
77#define crypto_core_salsa20_tweet_INPUTBYTES 16
78#define crypto_core_salsa20_tweet_KEYBYTES 32
79#define crypto_core_salsa20_tweet_CONSTBYTES 16
80extern int crypto_core_salsa20_tweet(unsigned char *,const unsigned char *,const unsigned char *,const unsigned char *);
81#define crypto_core_salsa20_tweet_VERSION "-"
82#define crypto_core_salsa20 crypto_core_salsa20_tweet
83#define crypto_core_salsa20_OUTPUTBYTES crypto_core_salsa20_tweet_OUTPUTBYTES
84#define crypto_core_salsa20_INPUTBYTES crypto_core_salsa20_tweet_INPUTBYTES
85#define crypto_core_salsa20_KEYBYTES crypto_core_salsa20_tweet_KEYBYTES
86#define crypto_core_salsa20_CONSTBYTES crypto_core_salsa20_tweet_CONSTBYTES
87#define crypto_core_salsa20_VERSION crypto_core_salsa20_tweet_VERSION
88#define crypto_core_salsa20_IMPLEMENTATION "crypto_core/salsa20/tweet"
89#define crypto_core_hsalsa20_tweet_OUTPUTBYTES 32
90#define crypto_core_hsalsa20_tweet_INPUTBYTES 16
91#define crypto_core_hsalsa20_tweet_KEYBYTES 32
92#define crypto_core_hsalsa20_tweet_CONSTBYTES 16
93extern int crypto_core_hsalsa20_tweet(unsigned char *,const unsigned char *,const unsigned char *,const unsigned char *);
94#define crypto_core_hsalsa20_tweet_VERSION "-"
95#define crypto_core_hsalsa20 crypto_core_hsalsa20_tweet
96#define crypto_core_hsalsa20_OUTPUTBYTES crypto_core_hsalsa20_tweet_OUTPUTBYTES
97#define crypto_core_hsalsa20_INPUTBYTES crypto_core_hsalsa20_tweet_INPUTBYTES
98#define crypto_core_hsalsa20_KEYBYTES crypto_core_hsalsa20_tweet_KEYBYTES
99#define crypto_core_hsalsa20_CONSTBYTES crypto_core_hsalsa20_tweet_CONSTBYTES
100#define crypto_core_hsalsa20_VERSION crypto_core_hsalsa20_tweet_VERSION
101#define crypto_core_hsalsa20_IMPLEMENTATION "crypto_core/hsalsa20/tweet"
102#define crypto_hashblocks_PRIMITIVE "sha512"
103#define crypto_hashblocks crypto_hashblocks_sha512
104#define crypto_hashblocks_STATEBYTES crypto_hashblocks_sha512_STATEBYTES
105#define crypto_hashblocks_BLOCKBYTES crypto_hashblocks_sha512_BLOCKBYTES
106#define crypto_hashblocks_IMPLEMENTATION crypto_hashblocks_sha512_IMPLEMENTATION
107#define crypto_hashblocks_VERSION crypto_hashblocks_sha512_VERSION
108#define crypto_hashblocks_sha512_tweet_STATEBYTES 64
109#define crypto_hashblocks_sha512_tweet_BLOCKBYTES 128
110extern int crypto_hashblocks_sha512_tweet(unsigned char *,const unsigned char *,unsigned long long);
111#define crypto_hashblocks_sha512_tweet_VERSION "-"
112#define crypto_hashblocks_sha512 crypto_hashblocks_sha512_tweet
113#define crypto_hashblocks_sha512_STATEBYTES crypto_hashblocks_sha512_tweet_STATEBYTES
114#define crypto_hashblocks_sha512_BLOCKBYTES crypto_hashblocks_sha512_tweet_BLOCKBYTES
115#define crypto_hashblocks_sha512_VERSION crypto_hashblocks_sha512_tweet_VERSION
116#define crypto_hashblocks_sha512_IMPLEMENTATION "crypto_hashblocks/sha512/tweet"
117#define crypto_hashblocks_sha256_tweet_STATEBYTES 32
118#define crypto_hashblocks_sha256_tweet_BLOCKBYTES 64
119extern int crypto_hashblocks_sha256_tweet(unsigned char *,const unsigned char *,unsigned long long);
120#define crypto_hashblocks_sha256_tweet_VERSION "-"
121#define crypto_hashblocks_sha256 crypto_hashblocks_sha256_tweet
122#define crypto_hashblocks_sha256_STATEBYTES crypto_hashblocks_sha256_tweet_STATEBYTES
123#define crypto_hashblocks_sha256_BLOCKBYTES crypto_hashblocks_sha256_tweet_BLOCKBYTES
124#define crypto_hashblocks_sha256_VERSION crypto_hashblocks_sha256_tweet_VERSION
125#define crypto_hashblocks_sha256_IMPLEMENTATION "crypto_hashblocks/sha256/tweet"
126#define crypto_hash_PRIMITIVE "sha512"
127#define crypto_hash crypto_hash_sha512
128#define crypto_hash_BYTES crypto_hash_sha512_BYTES
129#define crypto_hash_IMPLEMENTATION crypto_hash_sha512_IMPLEMENTATION
130#define crypto_hash_VERSION crypto_hash_sha512_VERSION
131#define crypto_hash_sha512_tweet_BYTES 64
132extern int crypto_hash_sha512_tweet(unsigned char *,const unsigned char *,unsigned long long);
133#define crypto_hash_sha512_tweet_VERSION "-"
134#define crypto_hash_sha512 crypto_hash_sha512_tweet
135#define crypto_hash_sha512_BYTES crypto_hash_sha512_tweet_BYTES
136#define crypto_hash_sha512_VERSION crypto_hash_sha512_tweet_VERSION
137#define crypto_hash_sha512_IMPLEMENTATION "crypto_hash/sha512/tweet"
138#define crypto_hash_sha256_tweet_BYTES 32
139extern int crypto_hash_sha256_tweet(unsigned char *,const unsigned char *,unsigned long long);
140#define crypto_hash_sha256_tweet_VERSION "-"
141#define crypto_hash_sha256 crypto_hash_sha256_tweet
142#define crypto_hash_sha256_BYTES crypto_hash_sha256_tweet_BYTES
143#define crypto_hash_sha256_VERSION crypto_hash_sha256_tweet_VERSION
144#define crypto_hash_sha256_IMPLEMENTATION "crypto_hash/sha256/tweet"
145#define crypto_onetimeauth_PRIMITIVE "poly1305"
146#define crypto_onetimeauth crypto_onetimeauth_poly1305
147#define crypto_onetimeauth_verify crypto_onetimeauth_poly1305_verify
148#define crypto_onetimeauth_BYTES crypto_onetimeauth_poly1305_BYTES
149#define crypto_onetimeauth_KEYBYTES crypto_onetimeauth_poly1305_KEYBYTES
150#define crypto_onetimeauth_IMPLEMENTATION crypto_onetimeauth_poly1305_IMPLEMENTATION
151#define crypto_onetimeauth_VERSION crypto_onetimeauth_poly1305_VERSION
152#define crypto_onetimeauth_poly1305_tweet_BYTES 16
153#define crypto_onetimeauth_poly1305_tweet_KEYBYTES 32
154extern int crypto_onetimeauth_poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *);
155extern int crypto_onetimeauth_poly1305_tweet_verify(const unsigned char *,const unsigned char *,unsigned long long,const unsigned char *);
156#define crypto_onetimeauth_poly1305_tweet_VERSION "-"
157#define crypto_onetimeauth_poly1305 crypto_onetimeauth_poly1305_tweet
158#define crypto_onetimeauth_poly1305_verify crypto_onetimeauth_poly1305_tweet_verify
159#define crypto_onetimeauth_poly1305_BYTES crypto_onetimeauth_poly1305_tweet_BYTES
160#define crypto_onetimeauth_poly1305_KEYBYTES crypto_onetimeauth_poly1305_tweet_KEYBYTES
161#define crypto_onetimeauth_poly1305_VERSION crypto_onetimeauth_poly1305_tweet_VERSION
162#define crypto_onetimeauth_poly1305_IMPLEMENTATION "crypto_onetimeauth/poly1305/tweet"
163#define crypto_scalarmult_PRIMITIVE "curve25519"
164#define crypto_scalarmult crypto_scalarmult_curve25519
165#define crypto_scalarmult_base crypto_scalarmult_curve25519_base
166#define crypto_scalarmult_BYTES crypto_scalarmult_curve25519_BYTES
167#define crypto_scalarmult_SCALARBYTES crypto_scalarmult_curve25519_SCALARBYTES
168#define crypto_scalarmult_IMPLEMENTATION crypto_scalarmult_curve25519_IMPLEMENTATION
169#define crypto_scalarmult_VERSION crypto_scalarmult_curve25519_VERSION
170#define crypto_scalarmult_curve25519_tweet_BYTES 32
171#define crypto_scalarmult_curve25519_tweet_SCALARBYTES 32
172extern int crypto_scalarmult_curve25519_tweet(unsigned char *,const unsigned char *,const unsigned char *);
173extern int crypto_scalarmult_curve25519_tweet_base(unsigned char *,const unsigned char *);
174#define crypto_scalarmult_curve25519_tweet_VERSION "-"
175#define crypto_scalarmult_curve25519 crypto_scalarmult_curve25519_tweet
176#define crypto_scalarmult_curve25519_base crypto_scalarmult_curve25519_tweet_base
177#define crypto_scalarmult_curve25519_BYTES crypto_scalarmult_curve25519_tweet_BYTES
178#define crypto_scalarmult_curve25519_SCALARBYTES crypto_scalarmult_curve25519_tweet_SCALARBYTES
179#define crypto_scalarmult_curve25519_VERSION crypto_scalarmult_curve25519_tweet_VERSION
180#define crypto_scalarmult_curve25519_IMPLEMENTATION "crypto_scalarmult/curve25519/tweet"
181#define crypto_secretbox_PRIMITIVE "xsalsa20poly1305"
182#define crypto_secretbox crypto_secretbox_xsalsa20poly1305
183#define crypto_secretbox_open crypto_secretbox_xsalsa20poly1305_open
184#define crypto_secretbox_KEYBYTES crypto_secretbox_xsalsa20poly1305_KEYBYTES
185#define crypto_secretbox_NONCEBYTES crypto_secretbox_xsalsa20poly1305_NONCEBYTES
186#define crypto_secretbox_ZEROBYTES crypto_secretbox_xsalsa20poly1305_ZEROBYTES
187#define crypto_secretbox_BOXZEROBYTES crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES
188#define crypto_secretbox_IMPLEMENTATION crypto_secretbox_xsalsa20poly1305_IMPLEMENTATION
189#define crypto_secretbox_VERSION crypto_secretbox_xsalsa20poly1305_VERSION
190#define crypto_secretbox_xsalsa20poly1305_tweet_KEYBYTES 32
191#define crypto_secretbox_xsalsa20poly1305_tweet_NONCEBYTES 24
192#define crypto_secretbox_xsalsa20poly1305_tweet_ZEROBYTES 32
193#define crypto_secretbox_xsalsa20poly1305_tweet_BOXZEROBYTES 16
194extern int crypto_secretbox_xsalsa20poly1305_tweet(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *);
195extern int crypto_secretbox_xsalsa20poly1305_tweet_open(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *);
196#define crypto_secretbox_xsalsa20poly1305_tweet_VERSION "-"
197#define crypto_secretbox_xsalsa20poly1305 crypto_secretbox_xsalsa20poly1305_tweet
198#define crypto_secretbox_xsalsa20poly1305_open crypto_secretbox_xsalsa20poly1305_tweet_open
199#define crypto_secretbox_xsalsa20poly1305_KEYBYTES crypto_secretbox_xsalsa20poly1305_tweet_KEYBYTES
200#define crypto_secretbox_xsalsa20poly1305_NONCEBYTES crypto_secretbox_xsalsa20poly1305_tweet_NONCEBYTES
201#define crypto_secretbox_xsalsa20poly1305_ZEROBYTES crypto_secretbox_xsalsa20poly1305_tweet_ZEROBYTES
202#define crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES crypto_secretbox_xsalsa20poly1305_tweet_BOXZEROBYTES
203#define crypto_secretbox_xsalsa20poly1305_VERSION crypto_secretbox_xsalsa20poly1305_tweet_VERSION
204#define crypto_secretbox_xsalsa20poly1305_IMPLEMENTATION "crypto_secretbox/xsalsa20poly1305/tweet"
205#define crypto_sign_PRIMITIVE "ed25519"
206#define crypto_sign crypto_sign_ed25519
207#define crypto_sign_open crypto_sign_ed25519_open
208#define crypto_sign_keypair crypto_sign_ed25519_keypair
209#define crypto_sign_BYTES crypto_sign_ed25519_BYTES
210#define crypto_sign_PUBLICKEYBYTES crypto_sign_ed25519_PUBLICKEYBYTES
211#define crypto_sign_SECRETKEYBYTES crypto_sign_ed25519_SECRETKEYBYTES
212#define crypto_sign_IMPLEMENTATION crypto_sign_ed25519_IMPLEMENTATION
213#define crypto_sign_VERSION crypto_sign_ed25519_VERSION
214#define crypto_sign_ed25519_tweet_BYTES 64
215#define crypto_sign_ed25519_tweet_PUBLICKEYBYTES 32
216#define crypto_sign_ed25519_tweet_SECRETKEYBYTES 64
217extern int crypto_sign_ed25519_tweet(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *);
218extern int crypto_sign_ed25519_tweet_open(unsigned char *,unsigned long long *,const unsigned char *,unsigned long long,const unsigned char *);
219extern int crypto_sign_ed25519_tweet_keypair(unsigned char *,unsigned char *);
220#define crypto_sign_ed25519_tweet_VERSION "-"
221#define crypto_sign_ed25519 crypto_sign_ed25519_tweet
222#define crypto_sign_ed25519_open crypto_sign_ed25519_tweet_open
223#define crypto_sign_ed25519_keypair crypto_sign_ed25519_tweet_keypair
224#define crypto_sign_ed25519_BYTES crypto_sign_ed25519_tweet_BYTES
225#define crypto_sign_ed25519_PUBLICKEYBYTES crypto_sign_ed25519_tweet_PUBLICKEYBYTES
226#define crypto_sign_ed25519_SECRETKEYBYTES crypto_sign_ed25519_tweet_SECRETKEYBYTES
227#define crypto_sign_ed25519_VERSION crypto_sign_ed25519_tweet_VERSION
228#define crypto_sign_ed25519_IMPLEMENTATION "crypto_sign/ed25519/tweet"
229#define crypto_stream_PRIMITIVE "xsalsa20"
230#define crypto_stream crypto_stream_xsalsa20
231#define crypto_stream_xor crypto_stream_xsalsa20_xor
232#define crypto_stream_KEYBYTES crypto_stream_xsalsa20_KEYBYTES
233#define crypto_stream_NONCEBYTES crypto_stream_xsalsa20_NONCEBYTES
234#define crypto_stream_IMPLEMENTATION crypto_stream_xsalsa20_IMPLEMENTATION
235#define crypto_stream_VERSION crypto_stream_xsalsa20_VERSION
236#define crypto_stream_xsalsa20_tweet_KEYBYTES 32
237#define crypto_stream_xsalsa20_tweet_NONCEBYTES 24
238extern int crypto_stream_xsalsa20_tweet(unsigned char *,unsigned long long,const unsigned char *,const unsigned char *);
239extern int crypto_stream_xsalsa20_tweet_xor(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *);
240#define crypto_stream_xsalsa20_tweet_VERSION "-"
241#define crypto_stream_xsalsa20 crypto_stream_xsalsa20_tweet
242#define crypto_stream_xsalsa20_xor crypto_stream_xsalsa20_tweet_xor
243#define crypto_stream_xsalsa20_KEYBYTES crypto_stream_xsalsa20_tweet_KEYBYTES
244#define crypto_stream_xsalsa20_NONCEBYTES crypto_stream_xsalsa20_tweet_NONCEBYTES
245#define crypto_stream_xsalsa20_VERSION crypto_stream_xsalsa20_tweet_VERSION
246#define crypto_stream_xsalsa20_IMPLEMENTATION "crypto_stream/xsalsa20/tweet"
247#define crypto_stream_salsa20_tweet_KEYBYTES 32
248#define crypto_stream_salsa20_tweet_NONCEBYTES 8
249extern int crypto_stream_salsa20_tweet(unsigned char *,unsigned long long,const unsigned char *,const unsigned char *);
250extern int crypto_stream_salsa20_tweet_xor(unsigned char *,const unsigned char *,unsigned long long,const unsigned char *,const unsigned char *);
251#define crypto_stream_salsa20_tweet_VERSION "-"
252#define crypto_stream_salsa20 crypto_stream_salsa20_tweet
253#define crypto_stream_salsa20_xor crypto_stream_salsa20_tweet_xor
254#define crypto_stream_salsa20_KEYBYTES crypto_stream_salsa20_tweet_KEYBYTES
255#define crypto_stream_salsa20_NONCEBYTES crypto_stream_salsa20_tweet_NONCEBYTES
256#define crypto_stream_salsa20_VERSION crypto_stream_salsa20_tweet_VERSION
257#define crypto_stream_salsa20_IMPLEMENTATION "crypto_stream/salsa20/tweet"
258#define crypto_verify_PRIMITIVE "16"
259#define crypto_verify crypto_verify_16
260#define crypto_verify_BYTES crypto_verify_16_BYTES
261#define crypto_verify_IMPLEMENTATION crypto_verify_16_IMPLEMENTATION
262#define crypto_verify_VERSION crypto_verify_16_VERSION
263#define crypto_verify_16_tweet_BYTES 16
264extern int crypto_verify_16_tweet(const unsigned char *,const unsigned char *);
265#define crypto_verify_16_tweet_VERSION "-"
266#define crypto_verify_16 crypto_verify_16_tweet
267#define crypto_verify_16_BYTES crypto_verify_16_tweet_BYTES
268#define crypto_verify_16_VERSION crypto_verify_16_tweet_VERSION
269#define crypto_verify_16_IMPLEMENTATION "crypto_verify/16/tweet"
270#define crypto_verify_32_tweet_BYTES 32
271extern int crypto_verify_32_tweet(const unsigned char *,const unsigned char *);
272#define crypto_verify_32_tweet_VERSION "-"
273#define crypto_verify_32 crypto_verify_32_tweet
274#define crypto_verify_32_BYTES crypto_verify_32_tweet_BYTES
275#define crypto_verify_32_VERSION crypto_verify_32_tweet_VERSION
276#define crypto_verify_32_IMPLEMENTATION "crypto_verify/32/tweet"
277#endif