aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlexey Shchepin <alexey@process-one.net>2016-05-12 18:32:13 +0300
committerAlexey Shchepin <alexey@process-one.net>2016-05-13 17:56:48 +0300
commit792f47b4bd3c4f423fd25c31b5f8ae82ac59b28b (patch)
treeaed1938b1868878cc3463ada565c8ad05b9c05e6 /src
parentFix C2S session data leak (#1078) (diff)
Update SQL escaping
Diffstat (limited to 'src')
-rw-r--r--src/ejabberd_sql.erl39
-rw-r--r--src/mod_vcard_sql.erl6
-rw-r--r--src/node_flat_sql.erl36
-rw-r--r--src/node_pep_sql.erl14
-rw-r--r--src/nodetree_tree_sql.erl8
-rw-r--r--src/sql_queries.erl8
6 files changed, 84 insertions, 27 deletions
diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl
index 09bd31973..f503a749f 100644
--- a/src/ejabberd_sql.erl
+++ b/src/ejabberd_sql.erl
@@ -43,6 +43,7 @@
escape/1,
escape_like/1,
escape_like_arg/1,
+ escape_like_arg_circumflex/1,
to_bool/1,
sqlite_db/1,
sqlite_file/1,
@@ -200,6 +201,7 @@ escape_like(S) when is_binary(S) ->
<< <<(escape_like(C))/binary>> || <<C>> <= S >>;
escape_like($%) -> <<"\\%">>;
escape_like($_) -> <<"\\_">>;
+escape_like($\\) -> <<"\\\\\\\\">>;
escape_like(C) when is_integer(C), C >= 0, C =< 255 -> sql_queries:escape(C).
escape_like_arg(S) when is_binary(S) ->
@@ -209,6 +211,13 @@ escape_like_arg($_) -> <<"\\_">>;
escape_like_arg($\\) -> <<"\\\\">>;
escape_like_arg(C) when is_integer(C), C >= 0, C =< 255 -> <<C>>.
+escape_like_arg_circumflex(S) when is_binary(S) ->
+ << <<(escape_like_arg_circumflex(C))/binary>> || <<C>> <= S >>;
+escape_like_arg_circumflex($%) -> <<"^%">>;
+escape_like_arg_circumflex($_) -> <<"^_">>;
+escape_like_arg_circumflex($^) -> <<"^^">>;
+escape_like_arg_circumflex(C) when is_integer(C), C >= 0, C =< 255 -> <<C>>.
+
to_bool(<<"t">>) -> true;
to_bool(<<"true">>) -> true;
to_bool(<<"1">>) -> true;
@@ -508,7 +517,7 @@ sql_query_internal(#sql_query{} = Query) ->
odbc ->
generic_sql_query(Query);
mssql ->
- generic_sql_query(Query);
+ mssql_sql_query(Query);
pgsql ->
Key = {?PREPARE_KEY, Query#sql_query.hash},
case get(Key) of
@@ -534,7 +543,7 @@ sql_query_internal(#sql_query{} = Query) ->
mysql ->
generic_sql_query(Query);
sqlite ->
- generic_sql_query(Query)
+ sqlite_sql_query(Query)
end
catch
Class:Reason ->
@@ -623,6 +632,32 @@ generic_escape() ->
end
}.
+sqlite_sql_query(SQLQuery) ->
+ sql_query_format_res(
+ sql_query_internal(sqlite_sql_query_format(SQLQuery)),
+ SQLQuery).
+
+sqlite_sql_query_format(SQLQuery) ->
+ Args = (SQLQuery#sql_query.args)(sqlite_escape()),
+ (SQLQuery#sql_query.format_query)(Args).
+
+sqlite_escape() ->
+ #sql_escape{string = fun(X) -> <<"'", (standard_escape(X))/binary, "'">> end,
+ integer = fun(X) -> integer_to_binary(X) end,
+ boolean = fun(true) -> <<"1">>;
+ (false) -> <<"0">>
+ end
+ }.
+
+standard_escape(S) ->
+ << <<(case Char of
+ $' -> << "''" >>;
+ _ -> << Char >>
+ end)/binary>> || <<Char>> <= S >>.
+
+mssql_sql_query(SQLQuery) ->
+ sqlite_sql_query(SQLQuery).
+
pgsql_prepare(SQLQuery, State) ->
Escape = #sql_escape{_ = fun(X) -> X end},
N = length((SQLQuery#sql_query.args)(Escape)),
diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl
index 6b8e90333..b8234bf9c 100644
--- a/src/mod_vcard_sql.erl
+++ b/src/mod_vcard_sql.erl
@@ -227,9 +227,11 @@ make_val(Match, Field, Val) ->
Condition = case str:suffix(<<"*">>, Val) of
true ->
Val1 = str:substr(Val, 1, byte_size(Val) - 1),
- SVal = <<(ejabberd_sql:escape_like(Val1))/binary,
+ SVal = <<(ejabberd_sql:escape(
+ ejabberd_sql:escape_like_arg_circumflex(
+ Val1)))/binary,
"%">>,
- [Field, <<" LIKE '">>, SVal, <<"'">>];
+ [Field, <<" LIKE '">>, SVal, <<"' ESCAPE '^'">>];
_ ->
SVal = ejabberd_sql:escape(Val),
[Field, <<" = '">>, SVal, <<"'">>]
diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl
index 5baabcfcf..ea3aa7d3c 100644
--- a/src/node_flat_sql.erl
+++ b/src/node_flat_sql.erl
@@ -51,9 +51,11 @@
get_entity_subscriptions_for_send_last/2, get_last_items/3]).
-export([decode_jid/1, encode_jid/1,
+ encode_jid_like/1,
decode_affiliation/1, decode_subscriptions/1,
encode_affiliation/1, encode_subscriptions/1,
- encode_host/1]).
+ encode_host/1,
+ encode_host_like/1]).
init(_Host, _ServerHost, _Opts) ->
%%pubsub_subscription_sql:init(),
@@ -350,11 +352,13 @@ get_entity_subscriptions(Host, Owner) ->
H = encode_host(Host),
SJ = encode_jid(SubKey),
GJ = encode_jid(GenKey),
+ GJLike = encode_jid_like(GenKey),
Query = case SubKey of
GenKey ->
[<<"select node, type, i.nodeid, jid, subscriptions "
"from pubsub_state i, pubsub_node n "
- "where i.nodeid = n.nodeid and jid like '">>, GJ, <<"%' and host='">>, H, <<"';">>];
+ "where i.nodeid = n.nodeid and jid like '">>, GJLike,
+ <<"%' escape '^' and host='">>, H, <<"';">>];
_ ->
[<<"select node, type, i.nodeid, jid, subscriptions "
"from pubsub_state i, pubsub_node n "
@@ -399,12 +403,14 @@ get_entity_subscriptions_for_send_last(Host, Owner) ->
H = encode_host(Host),
SJ = encode_jid(SubKey),
GJ = encode_jid(GenKey),
+ GJLike = encode_jid_like(GenKey),
Query = case SubKey of
GenKey ->
[<<"select node, type, i.nodeid, jid, subscriptions "
"from pubsub_state i, pubsub_node n, pubsub_node_option o "
"where i.nodeid = n.nodeid and n.nodeid = o.nodeid and name='send_last_published_item' "
- "and val='on_sub_and_presence' and jid like '">>, GJ, <<"%' and host='">>, H, <<"';">>];
+ "and val='on_sub_and_presence' and jid like '">>, GJLike,
+ <<"%' escape '^' and host='">>, H, <<"';">>];
_ ->
[<<"select node, type, i.nodeid, jid, subscriptions "
"from pubsub_state i, pubsub_node n, pubsub_node_option o "
@@ -568,8 +574,9 @@ get_states(Nidx) ->
[<<"jid">>, <<"affiliation">>, <<"subscriptions">>], RItems} ->
{result,
lists:map(fun ([SJID, Aff, Subs]) ->
- #pubsub_state{stateid = {decode_jid(SJID), Nidx},
- items = itemids(Nidx, SJID),
+ JID = decode_jid(SJID),
+ #pubsub_state{stateid = {JID, Nidx},
+ items = itemids(Nidx, JID),
affiliation = decode_affiliation(Aff),
subscriptions = decode_subscriptions(Subs)}
end,
@@ -899,13 +906,12 @@ first_in_list(Pred, [H | T]) ->
_ -> first_in_list(Pred, T)
end.
-itemids(Nidx, {U, S, R}) ->
- itemids(Nidx, encode_jid({U, S, R}));
-itemids(Nidx, SJID) ->
+itemids(Nidx, {_U, _S, _R} = JID) ->
+ SJID = encode_jid_like(JID),
case catch
ejabberd_sql:sql_query_t([<<"select itemid from pubsub_item where "
"nodeid='">>, Nidx, <<"' and publisher like '">>, SJID,
- <<"%' order by modification desc;">>])
+ <<"%' escape '^' order by modification desc;">>])
of
{selected, [<<"itemid">>], RItems} ->
[ItemId || [ItemId] <- RItems];
@@ -1011,6 +1017,10 @@ decode_subscriptions(Subscriptions) ->
encode_jid(JID) ->
ejabberd_sql:escape(jid:to_string(JID)).
+-spec(encode_jid_like/1 :: (JID :: ljid()) -> binary()).
+encode_jid_like(JID) ->
+ ejabberd_sql:escape(ejabberd_sql:escape_like_arg_circumflex(jid:to_string(JID))).
+
-spec(encode_host/1 ::
( Host :: host())
-> binary()
@@ -1018,6 +1028,14 @@ encode_jid(JID) ->
encode_host({_U, _S, _R} = LJID) -> encode_jid(LJID);
encode_host(Host) -> ejabberd_sql:escape(Host).
+-spec(encode_host_like/1 ::
+ ( Host :: host())
+ -> binary()
+ ).
+encode_host_like({_U, _S, _R} = LJID) -> encode_jid_like(LJID);
+encode_host_like(Host) ->
+ ejabberd_sql:escape(ejabberd_sql:escape_like_arg_circumflex(Host)).
+
-spec(encode_affiliation/1 ::
( Arg :: atom())
-> binary()
diff --git a/src/node_pep_sql.erl b/src/node_pep_sql.erl
index c5b31d15f..1f2c13d5c 100644
--- a/src/node_pep_sql.erl
+++ b/src/node_pep_sql.erl
@@ -114,20 +114,21 @@ set_affiliation(Nidx, Owner, Affiliation) ->
get_entity_subscriptions(_Host, Owner) ->
SubKey = jid:tolower(Owner),
GenKey = jid:remove_resource(SubKey),
- Host = node_flat_sql:encode_host(element(2, SubKey)),
+ HostLike = node_flat_sql:encode_host_like(element(2, SubKey)),
SJ = node_flat_sql:encode_jid(SubKey),
GJ = node_flat_sql:encode_jid(GenKey),
+ GJLike = node_flat_sql:encode_jid_like(GenKey),
Query = case SubKey of
GenKey ->
[<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_node n "
"where i.nodeid = n.nodeid and jid "
- "like '">>, GJ, <<"%' and host like '%@">>, Host, <<"';">>];
+ "like '">>, GJLike, <<"%' escape '^' and host like '%@">>, HostLike, <<"' escape '^';">>];
_ ->
[<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_node n "
"where i.nodeid = n.nodeid and jid "
- "in ('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host, <<"';">>]
+ "in ('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, HostLike, <<"' escape '^';">>]
end,
Reply = case catch ejabberd_sql:sql_query_t(Query) of
{selected,
@@ -149,9 +150,10 @@ get_entity_subscriptions(_Host, Owner) ->
get_entity_subscriptions_for_send_last(_Host, Owner) ->
SubKey = jid:tolower(Owner),
GenKey = jid:remove_resource(SubKey),
- Host = node_flat_sql:encode_host(element(2, SubKey)),
+ HostLike = node_flat_sql:encode_host_like(element(2, SubKey)),
SJ = node_flat_sql:encode_jid(SubKey),
GJ = node_flat_sql:encode_jid(GenKey),
+ GJLike = node_flat_sql:encode_jid_like(GenKey),
Query = case SubKey of
GenKey ->
[<<"select host, node, type, i.nodeid, jid, "
@@ -159,14 +161,14 @@ get_entity_subscriptions_for_send_last(_Host, Owner) ->
"pubsub_node_option o where i.nodeid = n.nodeid "
"and n.nodeid = o.nodeid and name='send_last_published_item' and "
"val='on_sub_and_presence' and jid like '">>,
- GJ, <<"%' and host like '%@">>, Host, <<"';">>];
+ GJLike, <<"%' escape '^' and host like '%@">>, HostLike, <<"' escape '^';">>];
_ ->
[<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_node n, "
"pubsub_node_option o where i.nodeid = n.nodeid "
"and n.nodeid = o.nodeid and name='send_last_published_item' and "
"val='on_sub_and_presence' and jid in ",
- "('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host, <<"';">>]
+ "('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, HostLike, <<"' escape '^';">>]
end,
Reply = case catch ejabberd_sql:sql_query_t(Query) of
{selected,
diff --git a/src/nodetree_tree_sql.erl b/src/nodetree_tree_sql.erl
index b56543395..17ae91b52 100644
--- a/src/nodetree_tree_sql.erl
+++ b/src/nodetree_tree_sql.erl
@@ -196,11 +196,11 @@ get_subnodes_tree(Host, Node, _From) ->
get_subnodes_tree(Host, Node) ->
H = node_flat_sql:encode_host(Host),
- N = ejabberd_sql:escape(Node),
+ N = ejabberd_sql:escape(ejabberd_sql:escape_like_arg_circumflex(Node)),
case catch
ejabberd_sql:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>,
- H, <<"' and node like '">>, N, <<"%';">>])
+ H, <<"' and node like '">>, N, <<"%' escape '^';">>])
of
{selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} ->
@@ -256,10 +256,10 @@ create_node(Host, Node, Type, Owner, Options, Parents) ->
delete_node(Host, Node) ->
H = node_flat_sql:encode_host(Host),
- N = ejabberd_sql:escape(Node),
+ N = ejabberd_sql:escape(ejabberd_sql:escape_like_arg_circumflex(Node)),
Removed = get_subnodes_tree(Host, Node),
catch ejabberd_sql:sql_query_t([<<"delete from pubsub_node where host='">>,
- H, <<"' and node like '">>, N, <<"%';">>]),
+ H, <<"' and node like '">>, N, <<"%' escape '^';">>]),
Removed.
%% helpers
diff --git a/src/sql_queries.erl b/src/sql_queries.erl
index e1374a817..121117574 100644
--- a/src/sql_queries.erl
+++ b/src/sql_queries.erl
@@ -231,12 +231,12 @@ list_users(LServer,
[{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
when is_binary(Prefix) and is_integer(Limit) and
is_integer(Offset) ->
- SPrefix = ejabberd_sql:escape_like_arg(Prefix),
+ SPrefix = ejabberd_sql:escape_like_arg_circumflex(Prefix),
SPrefix2 = <<SPrefix/binary, $%>>,
ejabberd_sql:sql_query(
LServer,
?SQL("select @(username)s from users "
- "where username like %(SPrefix2)s "
+ "where username like %(SPrefix2)s escape '^' "
"order by username "
"limit %(Limit)d offset %(Offset)d")).
@@ -264,12 +264,12 @@ users_number(LServer) ->
users_number(LServer, [{prefix, Prefix}])
when is_binary(Prefix) ->
- SPrefix = ejabberd_sql:escape_like_arg(Prefix),
+ SPrefix = ejabberd_sql:escape_like_arg_circumflex(Prefix),
SPrefix2 = <<SPrefix/binary, $%>>,
ejabberd_sql:sql_query(
LServer,
?SQL("select @(count(*))d from users "
- "where username like %(SPrefix2)s"));
+ "where username like %(SPrefix2)s escape '^'"));
users_number(LServer, []) ->
users_number(LServer).