summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore27
-rw-r--r--Changelog10
-rw-r--r--config.m47
-rw-r--r--execute.c209
-rw-r--r--php_suhosin.h45
-rw-r--r--suhosin.c15
-rw-r--r--tests/sql/connect.inc14
-rw-r--r--tests/sql/mysqli_comment_conditional.phpt25
-rw-r--r--tests/sql/mysqli_comment_cstyle_fail.phpt25
-rw-r--r--tests/sql/mysqli_comment_hashstyle_fail.phpt25
-rw-r--r--tests/sql/mysqli_comment_sqlstyle.phpt25
-rw-r--r--tests/sql/mysqli_comment_sqlstyle_fail.phpt25
-rw-r--r--tests/sql/mysqli_connect_invalid_username.phpt17
-rw-r--r--tests/sql/mysqli_multiselect.phpt25
-rw-r--r--tests/sql/mysqli_multiselect_fail.phpt25
-rw-r--r--tests/sql/mysqli_multiselect_subselect.phpt25
-rw-r--r--tests/sql/mysqli_no_constraints.phpt26
-rw-r--r--tests/sql/mysqli_open_comment.phpt25
-rw-r--r--tests/sql/mysqli_open_comment_fail.phpt25
-rw-r--r--tests/sql/mysqli_union.phpt26
-rw-r--r--tests/sql/mysqli_union_fail.phpt25
-rw-r--r--tests/sql/mysqli_user_match_error.phpt18
-rw-r--r--tests/sql/mysqli_user_match_ok.phpt18
-rw-r--r--tests/sql/mysqli_user_postfix.phpt18
-rw-r--r--tests/sql/mysqli_user_prefix.phpt18
-rw-r--r--tests/sql/skipifmysqli.inc5
26 files changed, 649 insertions, 99 deletions
diff --git a/.gitignore b/.gitignore
index e43b0f9..ddb1030 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,28 @@
1.DS_Store 1.DS_Store
2/config.log
3/config.guess
4/config.h
5/config.h.in
6/config.h.in~
7/config.nice
8/config.status
9/config.sub
10/*.lo
11/.deps
12/.libs/
13/Makefile
14/Makefile.*
15/ac*.m4
16/autom4te.cache/
17/build/
18/configure
19/configure.in
20/install-sh
21/libtool
22/ltmain.sh
23/missing
24/mkinstalldirs
25/modules/
26/run-tests.php
27/suhosin.la
28
diff --git a/Changelog b/Changelog
index 2bad2b3..a603fb7 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,13 @@
12014-06-24 - 0.9.37-dev
2
3 - Added SQL injection protection for Mysqli and several test cases
4 - Added wildcard matching for SQL username
5 - Added check for SQL username to only contain valid characters (>= ASCII 32)
6 - Test cases for user_prefix and user_postfix
7 - Added experimental PDO support
8 - SQL checks other than mysql (Mysqli + old-style) must be enabled with
9 configure --enable-suhosin-experimental, e.g. MSSQL.
10
12014-06-10 - 0.9.36 112014-06-10 - 0.9.36
2 12
3 - Added better handling of non existing/non executable shell scripts 13 - Added better handling of non existing/non executable shell scripts
diff --git a/config.m4 b/config.m4
index ee4b7ba..01edafd 100644
--- a/config.m4
+++ b/config.m4
@@ -7,3 +7,10 @@ PHP_ARG_ENABLE(suhosin, whether to enable suhosin support,
7if test "$PHP_SUHOSIN" != "no"; then 7if test "$PHP_SUHOSIN" != "no"; then
8 PHP_NEW_EXTENSION(suhosin, suhosin.c sha256.c memory_limit.c treat_data.c ifilter.c post_handler.c ufilter.c rfc1867.c rfc1867_new.c log.c header.c execute.c ex_imp.c session.c aes.c compat_snprintf.c, $ext_shared) 8 PHP_NEW_EXTENSION(suhosin, suhosin.c sha256.c memory_limit.c treat_data.c ifilter.c post_handler.c ufilter.c rfc1867.c rfc1867_new.c log.c header.c execute.c ex_imp.c session.c aes.c compat_snprintf.c, $ext_shared)
9fi 9fi
10
11PHP_ARG_ENABLE(suhosin-experimental, whether to enable experimental suhosin features,
12[ --enable-suhosin-experimental Enable experimental suhosin features], no, no)
13
14if test "$PHP_SUHOSIN_EXPERIMENTAL" != "no"; then
15 AC_DEFINE(SUHOSIN_EXPERIMENTAL, 1, [Whether to enable experimental suhosin features])
16fi
diff --git a/execute.c b/execute.c
index 1f7cf15..82a4866 100644
--- a/execute.c
+++ b/execute.c
@@ -24,6 +24,7 @@
24#endif 24#endif
25 25
26#include <fcntl.h> 26#include <fcntl.h>
27#include <fnmatch.h>
27#include "php.h" 28#include "php.h"
28#include "php_ini.h" 29#include "php_ini.h"
29#include "zend_hash.h" 30#include "zend_hash.h"
@@ -880,7 +881,7 @@ int ih_querycheck(IH_HANDLER_PARAMS)
880 return (0); 881 return (0);
881 } 882 }
882 883
883 if ((long) ih->arg1) { 884 if ((long) ih->arg2) {
884 mysql_extension = 1; 885 mysql_extension = 1;
885 } 886 }
886 887
@@ -892,6 +893,7 @@ int ih_querycheck(IH_HANDLER_PARAMS)
892 } 893 }
893 len = Z_STRLEN_P(backup); 894 len = Z_STRLEN_P(backup);
894 query = Z_STRVAL_P(backup); 895 query = Z_STRVAL_P(backup);
896 SDEBUG("SQL |%s|", query);
895 897
896 s = query; 898 s = query;
897 e = s+len; 899 e = s+len;
@@ -1023,29 +1025,16 @@ int ih_fixusername(IH_HANDLER_PARAMS)
1023 void **p = EG(argument_stack).top_element-2; 1025 void **p = EG(argument_stack).top_element-2;
1024#endif 1026#endif
1025 unsigned long arg_count; 1027 unsigned long arg_count;
1026 zval **arg;char *prefix, *postfix, *user; 1028 zval **arg;
1029 char *prefix, *postfix, *user, *user_match, *cp;
1027 zval *backup, *my_user; 1030 zval *backup, *my_user;
1028 int prefix_len, postfix_len, len; 1031 int prefix_len, postfix_len, len;
1029 1032
1030 SDEBUG("function: %s", ih->name); 1033 SDEBUG("function (fixusername): %s", ih->name);
1031 1034
1032 prefix = SUHOSIN_G(sql_user_prefix); 1035 prefix = SUHOSIN_G(sql_user_prefix);
1033 postfix = SUHOSIN_G(sql_user_postfix); 1036 postfix = SUHOSIN_G(sql_user_postfix);
1034 1037 user_match = SUHOSIN_G(sql_user_match);
1035 if ((prefix == NULL || prefix[0] == 0)&&
1036 (postfix == NULL || postfix[0] == 0)) {
1037 return (0);
1038 }
1039
1040 if (prefix == NULL) {
1041 prefix = "";
1042 }
1043 if (postfix == NULL) {
1044 postfix = "";
1045 }
1046
1047 prefix_len = strlen(prefix);
1048 postfix_len = strlen(postfix);
1049 1038
1050 arg_count = (unsigned long) *p; 1039 arg_count = (unsigned long) *p;
1051 1040
@@ -1064,26 +1053,60 @@ int ih_fixusername(IH_HANDLER_PARAMS)
1064 user = Z_STRVAL_P(backup); 1053 user = Z_STRVAL_P(backup);
1065 } 1054 }
1066 1055
1067 if (prefix_len && prefix_len <= len) { 1056 cp = user;
1068 if (strncmp(prefix, user, prefix_len)==0) { 1057 while (cp < user+len) {
1069 prefix = ""; 1058 if (*cp < 32) {
1070 len -= prefix_len; 1059 suhosin_log(S_SQL, "SQL username contains invalid characters");
1060 if (!SUHOSIN_G(simulation)) {
1061 RETVAL_FALSE;
1062 return (1);
1063 } else {
1064 break;
1065 }
1071 } 1066 }
1067 cp++;
1072 } 1068 }
1073 1069
1074 if (postfix_len && postfix_len <= len) { 1070 if ((prefix != NULL && prefix[0]) || (postfix != NULL && postfix[0])) {
1075 if (strncmp(postfix, user+len-postfix_len, postfix_len)==0) { 1071 if (prefix == NULL) {
1072 prefix = "";
1073 }
1074 if (postfix == NULL) {
1076 postfix = ""; 1075 postfix = "";
1077 } 1076 }
1077 prefix_len = strlen(prefix);
1078 postfix_len = strlen(postfix);
1079
1080 MAKE_STD_ZVAL(my_user);
1081 my_user->type = IS_STRING;
1082 my_user->value.str.len = spprintf(&my_user->value.str.val, 0, "%s%s%s", prefix, user, postfix);
1083
1084 /* XXX: memory_leak? */
1085 *arg = my_user;
1086
1087 len = Z_STRLEN_P(my_user);
1088 user = Z_STRVAL_P(my_user);
1078 } 1089 }
1079 1090
1080 MAKE_STD_ZVAL(my_user); 1091 if (user_match && user_match[0]) {
1081 my_user->type = IS_STRING; 1092#ifdef HAVE_FNMATCH
1082 my_user->value.str.len = spprintf(&my_user->value.str.val, 0, "%s%s%s", prefix, user, postfix); 1093 if (fnmatch(user_match, user, 0) != 0) {
1094 suhosin_log(S_SQL, "SQL username ('%s') does not match suhosin.sql.user_match ('%s')", user, user_match);
1095 if (!SUHOSIN_G(simulation)) {
1096 RETVAL_FALSE;
1097 return (1);
1098 }
1099 }
1100#else
1101#warning no support for fnmatch() - setting suhosin.sql.user_match will always fail.
1102 suhosin_log(S_SQL, "suhosin.sql.user_match specified, but system does not support fnmatch()");
1103 if (!SUHOSIN_G(simulation)) {
1104 RETVAL_FALSE;
1105 return (1);
1106 }
1107#endif
1108 }
1083 1109
1084 /* XXX: memory_leak? */
1085 *arg = my_user;
1086
1087 SDEBUG("function: %s - user: %s", ih->name, user); 1110 SDEBUG("function: %s - user: %s", ih->name, user);
1088 1111
1089 return (0); 1112 return (0);
@@ -1552,9 +1575,9 @@ static int ih_getrandmax(IH_HANDLER_PARAMS)
1552} 1575}
1553 1576
1554internal_function_handler ihandlers[] = { 1577internal_function_handler ihandlers[] = {
1555 { "preg_replace", ih_preg_replace, NULL, NULL, NULL }, 1578 { "preg_replace", ih_preg_replace, NULL, NULL, NULL },
1556 { "mail", ih_mail, NULL, NULL, NULL }, 1579 { "mail", ih_mail, NULL, NULL, NULL },
1557 { "symlink", ih_symlink, NULL, NULL, NULL }, 1580 { "symlink", ih_symlink, NULL, NULL, NULL },
1558 1581
1559 { "srand", ih_srand, NULL, NULL, NULL }, 1582 { "srand", ih_srand, NULL, NULL, NULL },
1560 { "mt_srand", ih_mt_srand, NULL, NULL, NULL }, 1583 { "mt_srand", ih_mt_srand, NULL, NULL, NULL },
@@ -1563,49 +1586,95 @@ internal_function_handler ihandlers[] = {
1563 { "getrandmax", ih_getrandmax, NULL, NULL, NULL }, 1586 { "getrandmax", ih_getrandmax, NULL, NULL, NULL },
1564 { "mt_getrandmax", ih_getrandmax, NULL, NULL, NULL }, 1587 { "mt_getrandmax", ih_getrandmax, NULL, NULL, NULL },
1565 1588
1566 { "ocilogon", ih_fixusername, (void *)1, NULL, NULL }, 1589 { "function_exists", ih_function_exists, NULL, NULL, NULL },
1567 { "ociplogon", ih_fixusername, (void *)1, NULL, NULL },
1568 { "ocinlogon", ih_fixusername, (void *)1, NULL, NULL },
1569 { "oci_connect", ih_fixusername, (void *)1, NULL, NULL },
1570 { "oci_pconnect", ih_fixusername, (void *)1, NULL, NULL },
1571 { "oci_new_connect", ih_fixusername, (void *)1, NULL, NULL },
1572 1590
1573 { "fbsql_change_user", ih_fixusername, (void *)1, NULL, NULL }, 1591 /* Mysqli */
1574 { "fbsql_connect", ih_fixusername, (void *)2, NULL, NULL }, 1592 { "mysqli::mysqli", ih_fixusername, (void *)2, NULL, NULL },
1575 { "fbsql_pconnect", ih_fixusername, (void *)2, NULL, NULL }, 1593 { "mysqli_connect", ih_fixusername, (void *)2, NULL, NULL },
1576 1594 { "mysqli::real_connect", ih_fixusername, (void *)2, NULL, NULL },
1577 { "function_exists", ih_function_exists, NULL, NULL, NULL }, 1595 { "mysqli_real_connect", ih_fixusername, (void *)3, NULL, NULL },
1596 { "mysqli_change_user", ih_fixusername, (void *)2, NULL, NULL },
1597 { "mysqli::change_user", ih_fixusername, (void *)1, NULL, NULL },
1598
1599 { "mysqli::query", ih_querycheck, (void *)1, (void *)1, NULL },
1600 { "mysqli_query", ih_querycheck, (void *)2, (void *)1, NULL },
1601 { "mysqli::multi_query", ih_querycheck, (void *)1, (void *)1, NULL },
1602 { "mysqli_multi_query", ih_querycheck, (void *)2, (void *)1, NULL },
1603 { "mysqli::prepare", ih_querycheck, (void *)1, (void *)1, NULL },
1604 { "mysqli_prepare", ih_querycheck, (void *)2, (void *)1, NULL },
1605 { "mysqli::real_query", ih_querycheck, (void *)1, (void *)1, NULL },
1606 { "mysqli_real_query", ih_querycheck, (void *)2, (void *)1, NULL },
1607 { "mysqli::send_query", ih_querycheck, (void *)1, (void *)1, NULL },
1608 { "mysqli_send_query", ih_querycheck, (void *)2, (void *)1, NULL },
1609 // removed in PHP 5.3
1610 { "mysqli_master_query", ih_querycheck, (void *)2, (void *)1, NULL },
1611 { "mysqli_slave_query", ih_querycheck, (void *)2, (void *)1, NULL },
1612 // ----
1578 1613
1579 { "ifx_connect", ih_fixusername, (void *)2, NULL, NULL }, 1614 /* Mysql API - deprecated in PHP 5.5 */
1580 { "ifx_pconnect", ih_fixusername, (void *)2, NULL, NULL }, 1615 { "mysql_connect", ih_fixusername, (void *)2, NULL, NULL },
1616 { "mysql_pconnect", ih_fixusername, (void *)2, NULL, NULL },
1617 { "mysql_query", ih_querycheck, (void *)1, (void *)1, NULL },
1618 { "mysql_db_query", ih_querycheck, (void *)2, (void *)1, NULL },
1619 { "mysql_unbuffered_query", ih_querycheck, (void *)1, (void *)1, NULL },
1620
1621#ifdef SUHOSIN_EXPERIMENTAL
1622 /* MaxDB */
1623 { "maxdb::maxdb", ih_fixusername, (void *)2, NULL, NULL },
1624 { "maxdb_connect", ih_fixusername, (void *)2, NULL, NULL },
1625 { "maxdb::real_connect", ih_fixusername, (void *)2, NULL, NULL },
1626 { "maxdb_real_connect", ih_fixusername, (void *)3, NULL, NULL },
1627 { "maxdb::change_user", ih_fixusername, (void *)1, NULL, NULL },
1628 { "maxdb_change_user", ih_fixusername, (void *)2, NULL, NULL },
1629
1630 { "maxdb_master_query", ih_querycheck, (void *)2, NULL, NULL },
1631 { "maxdb::multi_query", ih_querycheck, (void *)1, NULL, NULL },
1632 { "maxdb_multi_query", ih_querycheck, (void *)2, NULL, NULL },
1633 { "maxdb::query", ih_querycheck, (void *)1, NULL, NULL },
1634 { "maxdb_query", ih_querycheck, (void *)2, NULL, NULL },
1635 { "maxdb::real_query", ih_querycheck, (void *)1, NULL, NULL },
1636 { "maxdb_real_query", ih_querycheck, (void *)2, NULL, NULL },
1637 { "maxdb::send_query", ih_querycheck, (void *)1, NULL, NULL },
1638 { "maxdb_send_query", ih_querycheck, (void *)2, NULL, NULL },
1639 { "maxdb::prepare", ih_querycheck, (void *)1, NULL, NULL },
1640 { "maxdb_prepare", ih_querycheck, (void *)2, NULL, NULL },
1581 1641
1582 { "ibase_connect", ih_fixusername, (void *)2, NULL, NULL }, 1642 /* PDO */
1583 { "ibase_pconnect", ih_fixusername, (void *)2, NULL, NULL }, 1643 /* note: mysql conditional comments not supported here */
1644 { "pdo::__construct", ih_fixusername, (void *)2, NULL, NULL }, /* note: username may come from dsn (param 1) */
1645 { "pdo::query", ih_querycheck, (void *)1, NULL, NULL },
1646 { "pdo::prepare", ih_querycheck, (void *)1, NULL, NULL },
1647 { "pdo::exec", ih_querycheck, (void *)1, NULL, NULL },
1648
1649 /* Oracle OCI8 */
1650 { "ocilogon", ih_fixusername, (void *)1, NULL, NULL },
1651 { "ociplogon", ih_fixusername, (void *)1, NULL, NULL },
1652 { "ocinlogon", ih_fixusername, (void *)1, NULL, NULL },
1653 { "oci_connect", ih_fixusername, (void *)1, NULL, NULL },
1654 { "oci_pconnect", ih_fixusername, (void *)1, NULL, NULL },
1655 { "oci_new_connect", ih_fixusername, (void *)1, NULL, NULL },
1584 1656
1585 { "maxdb", ih_fixusername, (void *)2, NULL, NULL }, 1657 /* FrontBase */
1586 { "maxdb_change_user", ih_fixusername, (void *)2, NULL, NULL }, 1658 { "fbsql_connect", ih_fixusername, (void *)2, NULL, NULL },
1587 { "maxdb_connect", ih_fixusername, (void *)2, NULL, NULL }, 1659 { "fbsql_pconnect", ih_fixusername, (void *)2, NULL, NULL },
1588 { "maxdb_pconnect", ih_fixusername, (void *)2, NULL, NULL }, 1660 { "fbsql_change_user", ih_fixusername, (void *)1, NULL, NULL },
1589 { "maxdb_real_connect", ih_fixusername, (void *)3, NULL, NULL }, 1661 { "fbsql_username", ih_fixusername, (void *)2, NULL, NULL },
1590 1662
1591 { "mssql_connect", ih_fixusername, (void *)2, NULL, NULL }, 1663 /* Informix */
1592 { "mssql_pconnect", ih_fixusername, (void *)2, NULL, NULL }, 1664 { "ifx_connect", ih_fixusername, (void *)2, NULL, NULL },
1665 { "ifx_pconnect", ih_fixusername, (void *)2, NULL, NULL },
1593 1666
1594 { "mysql_query", ih_querycheck, (void *)1, (void *)1, NULL }, 1667 /* Firebird/InterBase */
1595 { "mysql_db_query", ih_querycheck, (void *)2, (void *)1, NULL }, 1668 { "ibase_connect", ih_fixusername, (void *)2, NULL, NULL },
1596 { "mysql_unbuffered_query", ih_querycheck, (void *)1, (void *)1, NULL }, 1669 { "ibase_pconnect", ih_fixusername, (void *)2, NULL, NULL },
1597 { "mysqli_query", ih_querycheck, (void *)2, (void *)1, NULL }, 1670 { "ibase_service_attach", ih_fixusername, (void *)2, NULL, NULL },
1598 { "mysqli_real_query", ih_querycheck, (void *)2, (void *)1, NULL }, 1671
1599 { "mysqli_send_query", ih_querycheck, (void *)2, (void *)1, NULL }, 1672 /* Microsoft SQL Server */
1600 { "mysqli_master_query", ih_querycheck, (void *)2, (void *)1, NULL }, 1673 { "mssql_connect", ih_fixusername, (void *)2, NULL, NULL },
1601 { "mysqli_slave_query", ih_querycheck, (void *)2, (void *)1, NULL }, 1674 { "mssql_pconnect", ih_fixusername, (void *)2, NULL, NULL },
1675#endif
1602 1676
1603 { "mysqli", ih_fixusername, (void *)2, NULL, NULL }, 1677 { NULL, NULL, NULL, NULL, NULL }
1604 { "mysql_connect", ih_fixusername, (void *)2, NULL, NULL },
1605 { "mysql_pconnect", ih_fixusername, (void *)2, NULL, NULL },
1606 { "mysqli_change_user", ih_fixusername, (void *)2, NULL, NULL },
1607 { "mysql_real_connect", ih_fixusername, (void *)3, NULL, NULL },
1608 { NULL, NULL, NULL, NULL, NULL }
1609}; 1678};
1610 1679
1611#define FUNCTION_WARNING() zend_error(E_WARNING, "%s() has been disabled for security reasons", get_active_function_name(TSRMLS_C)); 1680#define FUNCTION_WARNING() zend_error(E_WARNING, "%s() has been disabled for security reasons", get_active_function_name(TSRMLS_C));
diff --git a/php_suhosin.h b/php_suhosin.h
index 6d2ea80..4b460e4 100644
--- a/php_suhosin.h
+++ b/php_suhosin.h
@@ -76,15 +76,16 @@ ZEND_BEGIN_MODULE_GLOBALS(suhosin)
76 char *filter_action; 76 char *filter_action;
77 char *sql_user_prefix; 77 char *sql_user_prefix;
78 char *sql_user_postfix; 78 char *sql_user_postfix;
79 long sql_comment; 79 char *sql_user_match;
80 long sql_opencomment; 80 long sql_comment;
81 long sql_union; 81 long sql_opencomment;
82 long sql_mselect; 82 long sql_union;
83 long sql_mselect;
83 84
84 long max_execution_depth; 85 long max_execution_depth;
85 zend_bool abort_request; 86 zend_bool abort_request;
86 long executor_include_max_traversal; 87 long executor_include_max_traversal;
87 zend_bool executor_include_allow_writable_files; 88 zend_bool executor_include_allow_writable_files;
88 89
89 90
90 HashTable *include_whitelist; 91 HashTable *include_whitelist;
@@ -153,11 +154,11 @@ ZEND_BEGIN_MODULE_GLOBALS(suhosin)
153 zend_bool upload_allow_utf8; 154 zend_bool upload_allow_utf8;
154 char *upload_verification_script; 155 char *upload_verification_script;
155 156
156 zend_bool no_more_variables; 157 zend_bool no_more_variables;
157 zend_bool no_more_get_variables; 158 zend_bool no_more_get_variables;
158 zend_bool no_more_post_variables; 159 zend_bool no_more_post_variables;
159 zend_bool no_more_cookie_variables; 160 zend_bool no_more_cookie_variables;
160 zend_bool no_more_uploads; 161 zend_bool no_more_uploads;
161 162
162 163
163 164
@@ -199,8 +200,8 @@ ZEND_BEGIN_MODULE_GLOBALS(suhosin)
199 int (*old_s_destroy)(void **mod_data, const char *key TSRMLS_DC); 200 int (*old_s_destroy)(void **mod_data, const char *key TSRMLS_DC);
200 201
201 BYTE fi[24],ri[24]; 202 BYTE fi[24],ri[24];
202 WORD fkey[120]; 203 WORD fkey[120];
203 WORD rkey[120]; 204 WORD rkey[120];
204 205
205 zend_bool session_encrypt; 206 zend_bool session_encrypt;
206 char* session_cryptkey; 207 char* session_cryptkey;
@@ -248,16 +249,16 @@ ZEND_BEGIN_MODULE_GLOBALS(suhosin)
248 zend_bool mt_is_seeded; 249 zend_bool mt_is_seeded;
249 250
250 /* PERDIR Handling */ 251 /* PERDIR Handling */
251 char *perdir; 252 char *perdir;
252 zend_bool log_perdir; 253 zend_bool log_perdir;
253 zend_bool exec_perdir; 254 zend_bool exec_perdir;
254 zend_bool get_perdir; 255 zend_bool get_perdir;
255 zend_bool post_perdir; 256 zend_bool post_perdir;
256 zend_bool cookie_perdir; 257 zend_bool cookie_perdir;
257 zend_bool request_perdir; 258 zend_bool request_perdir;
258 zend_bool upload_perdir; 259 zend_bool upload_perdir;
259 zend_bool sql_perdir; 260 zend_bool sql_perdir;
260 zend_bool misc_perdir; 261 zend_bool misc_perdir;
261 262
262ZEND_END_MODULE_GLOBALS(suhosin) 263ZEND_END_MODULE_GLOBALS(suhosin)
263 264
diff --git a/suhosin.c b/suhosin.c
index f2533cb..f57e85a 100644
--- a/suhosin.c
+++ b/suhosin.c
@@ -990,13 +990,14 @@ PHP_INI_BEGIN()
990 STD_PHP_INI_ENTRY("suhosin.upload.verification_script", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateUploadString, upload_verification_script, zend_suhosin_globals, suhosin_globals) 990 STD_PHP_INI_ENTRY("suhosin.upload.verification_script", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateUploadString, upload_verification_script, zend_suhosin_globals, suhosin_globals)
991 991
992 992
993 STD_ZEND_INI_BOOLEAN("suhosin.sql.bailout_on_error", "0", ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateSQLBool, sql_bailout_on_error, zend_suhosin_globals, suhosin_globals) 993 STD_ZEND_INI_BOOLEAN("suhosin.sql.bailout_on_error", "0", ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateSQLBool, sql_bailout_on_error, zend_suhosin_globals, suhosin_globals)
994 STD_PHP_INI_ENTRY("suhosin.sql.user_prefix", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLString, sql_user_prefix, zend_suhosin_globals, suhosin_globals) 994 STD_PHP_INI_ENTRY("suhosin.sql.user_prefix", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLString, sql_user_prefix, zend_suhosin_globals, suhosin_globals)
995 STD_PHP_INI_ENTRY("suhosin.sql.user_postfix", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLString, sql_user_postfix, zend_suhosin_globals, suhosin_globals) 995 STD_PHP_INI_ENTRY("suhosin.sql.user_postfix", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLString, sql_user_postfix, zend_suhosin_globals, suhosin_globals)
996 STD_PHP_INI_ENTRY("suhosin.sql.comment", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLLong, sql_comment, zend_suhosin_globals, suhosin_globals) 996 STD_PHP_INI_ENTRY("suhosin.sql.user_match", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLString, sql_user_match, zend_suhosin_globals, suhosin_globals)
997 STD_PHP_INI_ENTRY("suhosin.sql.opencomment", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLLong, sql_opencomment, zend_suhosin_globals, suhosin_globals) 997 STD_PHP_INI_ENTRY("suhosin.sql.comment", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLLong, sql_comment, zend_suhosin_globals, suhosin_globals)
998 STD_PHP_INI_ENTRY("suhosin.sql.multiselect", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLLong, sql_mselect, zend_suhosin_globals, suhosin_globals) 998 STD_PHP_INI_ENTRY("suhosin.sql.opencomment", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLLong, sql_opencomment, zend_suhosin_globals, suhosin_globals)
999 STD_PHP_INI_ENTRY("suhosin.sql.union", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLLong, sql_union, zend_suhosin_globals, suhosin_globals) 999 STD_PHP_INI_ENTRY("suhosin.sql.multiselect", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLLong, sql_mselect, zend_suhosin_globals, suhosin_globals)
1000 STD_PHP_INI_ENTRY("suhosin.sql.union", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateSQLLong, sql_union, zend_suhosin_globals, suhosin_globals)
1000 1001
1001 STD_ZEND_INI_BOOLEAN("suhosin.session.encrypt", "1", ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateBool, session_encrypt, zend_suhosin_globals, suhosin_globals) 1002 STD_ZEND_INI_BOOLEAN("suhosin.session.encrypt", "1", ZEND_INI_PERDIR|ZEND_INI_SYSTEM, OnUpdateBool, session_encrypt, zend_suhosin_globals, suhosin_globals)
1002 STD_PHP_INI_ENTRY("suhosin.session.cryptkey", "", PHP_INI_ALL, OnUpdateString, session_cryptkey, zend_suhosin_globals, suhosin_globals) 1003 STD_PHP_INI_ENTRY("suhosin.session.cryptkey", "", PHP_INI_ALL, OnUpdateString, session_cryptkey, zend_suhosin_globals, suhosin_globals)
diff --git a/tests/sql/connect.inc b/tests/sql/connect.inc
new file mode 100644
index 0000000..367d63d
--- /dev/null
+++ b/tests/sql/connect.inc
@@ -0,0 +1,14 @@
1<?php
2
3 $host = getenv("MYSQL_TEST_HOST") ? getenv("MYSQL_TEST_HOST") : "localhost";
4 $port = getenv("MYSQL_TEST_PORT") ? getenv("MYSQL_TEST_PORT") : 3306;
5 $user = getenv("MYSQL_TEST_USER") ? getenv("MYSQL_TEST_USER") : "root";
6 $passwd = getenv("MYSQL_TEST_PASSWD") ? getenv("MYSQL_TEST_PASSWD") : "";
7 $db = getenv("MYSQL_TEST_DB") ? getenv("MYSQL_TEST_DB") : "test";
8 $socket = getenv("MYSQL_TEST_SOCKET") ? getenv("MYSQL_TEST_SOCKET") : null;
9
10 function connect_mysqli_oostyle() {
11 global $host, $port, $user, $passwd, $db, $socket;
12 return new mysqli($host, $user, $passwd, $db, $port, $socket);
13 }
14?> \ No newline at end of file
diff --git a/tests/sql/mysqli_comment_conditional.phpt b/tests/sql/mysqli_comment_conditional.phpt
new file mode 100644
index 0000000..0436c64
--- /dev/null
+++ b/tests/sql/mysqli_comment_conditional.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Mysqli query with SQL comment protection and MySQL condition (/*!...*/)
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=2
7suhosin.sql.opencomment=0
8suhosin.sql.multiselect=0
9suhosin.sql.union=0
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1 /*! ... */");
21flush();
22echo "mark.";
23?>
24--EXPECTF--
25mark. \ No newline at end of file
diff --git a/tests/sql/mysqli_comment_cstyle_fail.phpt b/tests/sql/mysqli_comment_cstyle_fail.phpt
new file mode 100644
index 0000000..56a8ccb
--- /dev/null
+++ b/tests/sql/mysqli_comment_cstyle_fail.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Mysqli query with SQL comment (/*...*/) protection set to fail
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=2
7suhosin.sql.opencomment=0
8suhosin.sql.multiselect=0
9suhosin.sql.union=0
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1 /* injection */");
21flush();
22echo "mark.";
23?>
24--EXPECTREGEX--
25ALERT - Comment in SQL query.*\) \ No newline at end of file
diff --git a/tests/sql/mysqli_comment_hashstyle_fail.phpt b/tests/sql/mysqli_comment_hashstyle_fail.phpt
new file mode 100644
index 0000000..6f5b517
--- /dev/null
+++ b/tests/sql/mysqli_comment_hashstyle_fail.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Mysqli query with SQL comment (#) protection set to fail
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=2
7suhosin.sql.opencomment=0
8suhosin.sql.multiselect=0
9suhosin.sql.union=0
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1 # injection");
21flush();
22echo "mark.";
23?>
24--EXPECTREGEX--
25ALERT - Comment in SQL query.*\) \ No newline at end of file
diff --git a/tests/sql/mysqli_comment_sqlstyle.phpt b/tests/sql/mysqli_comment_sqlstyle.phpt
new file mode 100644
index 0000000..c32c76a
--- /dev/null
+++ b/tests/sql/mysqli_comment_sqlstyle.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Mysqli query with SQL comment (--) protection
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=1
7suhosin.sql.opencomment=0
8suhosin.sql.multiselect=0
9suhosin.sql.union=0
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1 -- injection");
21flush();
22echo "mark.";
23?>
24--EXPECTREGEX--
25ALERT - Comment in SQL query.*mark. \ No newline at end of file
diff --git a/tests/sql/mysqli_comment_sqlstyle_fail.phpt b/tests/sql/mysqli_comment_sqlstyle_fail.phpt
new file mode 100644
index 0000000..83e63c5
--- /dev/null
+++ b/tests/sql/mysqli_comment_sqlstyle_fail.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Mysqli query with SQL comment (--) protection set to fail
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=2
7suhosin.sql.opencomment=0
8suhosin.sql.multiselect=0
9suhosin.sql.union=0
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1 -- injection");
21flush();
22echo "mark.";
23?>
24--EXPECTREGEX--
25ALERT - Comment in SQL query.*\) \ No newline at end of file
diff --git a/tests/sql/mysqli_connect_invalid_username.phpt b/tests/sql/mysqli_connect_invalid_username.phpt
new file mode 100644
index 0000000..532254f
--- /dev/null
+++ b/tests/sql/mysqli_connect_invalid_username.phpt
@@ -0,0 +1,17 @@
1--TEST--
2Mysqli connect with user_match not matching username
3--INI--
4extension=mysqli.so
5suhosin.log.stdout=32
6--SKIPIF--
7<?php
8include('skipifmysqli.inc');
9include('skipif.inc');
10?>
11--FILE--
12<?php
13include('connect.inc');
14$mysqli = new mysqli($host, "invalid\x01_username", $passwd, $db, $port, $socket);
15?>
16--EXPECTREGEX--
17ALERT - SQL username contains invalid characters.* \ No newline at end of file
diff --git a/tests/sql/mysqli_multiselect.phpt b/tests/sql/mysqli_multiselect.phpt
new file mode 100644
index 0000000..63d6c19
--- /dev/null
+++ b/tests/sql/mysqli_multiselect.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Mysqli query with multiple SELECT statements
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=0
7suhosin.sql.opencomment=0
8suhosin.sql.multiselect=1
9suhosin.sql.union=0
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1; SELECT 2");
21flush();
22echo "mark.";
23?>
24--EXPECTREGEX--
25ALERT - Multiple SELECT in SQL query.*mark. \ No newline at end of file
diff --git a/tests/sql/mysqli_multiselect_fail.phpt b/tests/sql/mysqli_multiselect_fail.phpt
new file mode 100644
index 0000000..2bee62a
--- /dev/null
+++ b/tests/sql/mysqli_multiselect_fail.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Mysqli query with multiple SELECT statements set to fail
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=0
7suhosin.sql.opencomment=0
8suhosin.sql.multiselect=2
9suhosin.sql.union=0
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1; SELECT 2");
21flush();
22echo "mark.";
23?>
24--EXPECTREGEX--
25ALERT - Multiple SELECT in SQL query.*\) \ No newline at end of file
diff --git a/tests/sql/mysqli_multiselect_subselect.phpt b/tests/sql/mysqli_multiselect_subselect.phpt
new file mode 100644
index 0000000..e629720
--- /dev/null
+++ b/tests/sql/mysqli_multiselect_subselect.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Mysqli query with sub-SELECT
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=0
7suhosin.sql.opencomment=0
8suhosin.sql.multiselect=1
9suhosin.sql.union=0
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT * FROM (SELECT 1)");
21flush();
22echo "mark.";
23?>
24--EXPECTREGEX--
25ALERT - Multiple SELECT in SQL query.*mark. \ No newline at end of file
diff --git a/tests/sql/mysqli_no_constraints.phpt b/tests/sql/mysqli_no_constraints.phpt
new file mode 100644
index 0000000..1d7fff6
--- /dev/null
+++ b/tests/sql/mysqli_no_constraints.phpt
@@ -0,0 +1,26 @@
1--TEST--
2Mysqli connection test without any constraints
3--INI--
4extension=mysqli.so
5suhosin.sql.comment=0
6suhosin.sql.bailout_on_error=0
7suhosin.sql.comment=0
8suhosin.sql.opencomment=0
9suhosin.sql.multiselect=0
10suhosin.sql.union=0
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1 AS A UNION SELECT 2 -- injection");
21$rows = $result->fetch_all();
22if ($rows !== null && count($rows) == 2) { echo "ok"; }
23
24?>
25--EXPECTF--
26ok \ No newline at end of file
diff --git a/tests/sql/mysqli_open_comment.phpt b/tests/sql/mysqli_open_comment.phpt
new file mode 100644
index 0000000..29d3536
--- /dev/null
+++ b/tests/sql/mysqli_open_comment.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Mysqli query with SQL open comment protection (/*...)
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=0
7suhosin.sql.opencomment=1
8suhosin.sql.multiselect=0
9suhosin.sql.union=0
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1 /*");
21flush();
22echo "mark.";
23?>
24--EXPECTREGEX--
25ALERT - Open comment in SQL query.*mark. \ No newline at end of file
diff --git a/tests/sql/mysqli_open_comment_fail.phpt b/tests/sql/mysqli_open_comment_fail.phpt
new file mode 100644
index 0000000..4645523
--- /dev/null
+++ b/tests/sql/mysqli_open_comment_fail.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Mysqli query with SQL open comment protection (/*...) set to fail
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=0
7suhosin.sql.opencomment=2
8suhosin.sql.multiselect=0
9suhosin.sql.union=0
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1 /*");
21flush();
22echo "mark.";
23?>
24--EXPECTREGEX--
25ALERT - Open comment in SQL query.*\) \ No newline at end of file
diff --git a/tests/sql/mysqli_union.phpt b/tests/sql/mysqli_union.phpt
new file mode 100644
index 0000000..9af9c61
--- /dev/null
+++ b/tests/sql/mysqli_union.phpt
@@ -0,0 +1,26 @@
1--TEST--
2Mysqli query with UNION protection
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=0
7suhosin.sql.opencomment=0
8suhosin.sql.multiselect=0
9suhosin.sql.union=1
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1 UNION SELECT 2");
21flush();
22echo "mark.";
23
24?>
25--EXPECTREGEX--
26ALERT - UNION in SQL query.*mark. \ No newline at end of file
diff --git a/tests/sql/mysqli_union_fail.phpt b/tests/sql/mysqli_union_fail.phpt
new file mode 100644
index 0000000..ee51a79
--- /dev/null
+++ b/tests/sql/mysqli_union_fail.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Mysqli query with UNION protection set to fail
3--INI--
4extension=mysqli.so
5suhosin.sql.bailout_on_error=0
6suhosin.sql.comment=0
7suhosin.sql.opencomment=0
8suhosin.sql.multiselect=0
9suhosin.sql.union=2
10suhosin.log.stdout=32
11--SKIPIF--
12<?php
13include('skipifmysqli.inc');
14include('skipif.inc');
15?>
16--FILE--
17<?php
18include('connect.inc');
19$mysqli = connect_mysqli_oostyle();
20$result = $mysqli->query("SELECT 1 UNION SELECT 2");
21echo "mark.";
22
23?>
24--EXPECTREGEX--
25ALERT - UNION in SQL query.*\) \ No newline at end of file
diff --git a/tests/sql/mysqli_user_match_error.phpt b/tests/sql/mysqli_user_match_error.phpt
new file mode 100644
index 0000000..69db081
--- /dev/null
+++ b/tests/sql/mysqli_user_match_error.phpt
@@ -0,0 +1,18 @@
1--TEST--
2Mysqli connect with user_match not matching username
3--INI--
4extension=mysqli.so
5suhosin.sql.user_match=complicated_userprefix*
6suhosin.log.stdout=32
7--SKIPIF--
8<?php
9include('skipifmysqli.inc');
10include('skipif.inc');
11?>
12--FILE--
13<?php
14include('connect.inc');
15$mysqli = new mysqli($host, 'invalid_username', $passwd, $db, $port, $socket);
16?>
17--EXPECTREGEX--
18ALERT - SQL username .* does not match.* \ No newline at end of file
diff --git a/tests/sql/mysqli_user_match_ok.phpt b/tests/sql/mysqli_user_match_ok.phpt
new file mode 100644
index 0000000..a2ad832
--- /dev/null
+++ b/tests/sql/mysqli_user_match_ok.phpt
@@ -0,0 +1,18 @@
1--TEST--
2Mysqli connect with user_match matching username
3--INI--
4extension=mysqli.so
5suhosin.sql.user_match=invalid_*
6suhosin.log.stdout=32
7--SKIPIF--
8<?php
9include('skipifmysqli.inc');
10include('skipif.inc');
11?>
12--FILE--
13<?php
14include('connect.inc');
15$mysqli = new mysqli($host, 'invalid_username', $passwd, $db, $port, $socket);
16?>
17--EXPECTREGEX--
18.*Access denied for user 'invalid_username'.* \ No newline at end of file
diff --git a/tests/sql/mysqli_user_postfix.phpt b/tests/sql/mysqli_user_postfix.phpt
new file mode 100644
index 0000000..11e3fe6
--- /dev/null
+++ b/tests/sql/mysqli_user_postfix.phpt
@@ -0,0 +1,18 @@
1--TEST--
2Mysqli connect with user_postfix
3--INI--
4extension=mysqli.so
5suhosin.sql.user_postfix=_post
6suhosin.log.stdout=32
7--SKIPIF--
8<?php
9include('skipifmysqli.inc');
10include('skipif.inc');
11?>
12--FILE--
13<?php
14include('connect.inc');
15$mysqli = new mysqli($host, 'invalid_username', $passwd, $db, $port, $socket);
16?>
17--EXPECTREGEX--
18.*Access denied for user 'invalid_username_post'.* \ No newline at end of file
diff --git a/tests/sql/mysqli_user_prefix.phpt b/tests/sql/mysqli_user_prefix.phpt
new file mode 100644
index 0000000..bb229f0
--- /dev/null
+++ b/tests/sql/mysqli_user_prefix.phpt
@@ -0,0 +1,18 @@
1--TEST--
2Mysqli connect with user_prefix
3--INI--
4extension=mysqli.so
5suhosin.sql.user_prefix=pre_
6suhosin.log.stdout=32
7--SKIPIF--
8<?php
9include('skipifmysqli.inc');
10include('skipif.inc');
11?>
12--FILE--
13<?php
14include('connect.inc');
15$mysqli = new mysqli($host, 'invalid_username', $passwd, $db, $port, $socket);
16?>
17--EXPECTREGEX--
18.*Access denied for user 'pre_invalid_username'.* \ No newline at end of file
diff --git a/tests/sql/skipifmysqli.inc b/tests/sql/skipifmysqli.inc
new file mode 100644
index 0000000..ee16cf1
--- /dev/null
+++ b/tests/sql/skipifmysqli.inc
@@ -0,0 +1,5 @@
1<?php
2if (!extension_loaded("mysqli")) {
3 die('skip - mysqli extension not available');
4}
5?> \ No newline at end of file