25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-24 16:23:40 +01:00

Update SQL escaping

This commit is contained in:
Alexey Shchepin 2016-05-12 18:32:13 +03:00
parent be2a9e35ae
commit 792f47b4bd
7 changed files with 85 additions and 28 deletions

View File

@ -43,6 +43,7 @@
escape/1, escape/1,
escape_like/1, escape_like/1,
escape_like_arg/1, escape_like_arg/1,
escape_like_arg_circumflex/1,
to_bool/1, to_bool/1,
sqlite_db/1, sqlite_db/1,
sqlite_file/1, sqlite_file/1,
@ -200,6 +201,7 @@ escape_like(S) when is_binary(S) ->
<< <<(escape_like(C))/binary>> || <<C>> <= S >>; << <<(escape_like(C))/binary>> || <<C>> <= S >>;
escape_like($%) -> <<"\\%">>; escape_like($%) -> <<"\\%">>;
escape_like($_) -> <<"\\_">>; escape_like($_) -> <<"\\_">>;
escape_like($\\) -> <<"\\\\\\\\">>;
escape_like(C) when is_integer(C), C >= 0, C =< 255 -> sql_queries:escape(C). escape_like(C) when is_integer(C), C >= 0, C =< 255 -> sql_queries:escape(C).
escape_like_arg(S) when is_binary(S) -> escape_like_arg(S) when is_binary(S) ->
@ -209,6 +211,13 @@ escape_like_arg($_) -> <<"\\_">>;
escape_like_arg($\\) -> <<"\\\\">>; escape_like_arg($\\) -> <<"\\\\">>;
escape_like_arg(C) when is_integer(C), C >= 0, C =< 255 -> <<C>>. 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(<<"t">>) -> true;
to_bool(<<"true">>) -> true; to_bool(<<"true">>) -> true;
to_bool(<<"1">>) -> true; to_bool(<<"1">>) -> true;
@ -508,7 +517,7 @@ sql_query_internal(#sql_query{} = Query) ->
odbc -> odbc ->
generic_sql_query(Query); generic_sql_query(Query);
mssql -> mssql ->
generic_sql_query(Query); mssql_sql_query(Query);
pgsql -> pgsql ->
Key = {?PREPARE_KEY, Query#sql_query.hash}, Key = {?PREPARE_KEY, Query#sql_query.hash},
case get(Key) of case get(Key) of
@ -534,7 +543,7 @@ sql_query_internal(#sql_query{} = Query) ->
mysql -> mysql ->
generic_sql_query(Query); generic_sql_query(Query);
sqlite -> sqlite ->
generic_sql_query(Query) sqlite_sql_query(Query)
end end
catch catch
Class:Reason -> Class:Reason ->
@ -623,6 +632,32 @@ generic_escape() ->
end 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) -> pgsql_prepare(SQLQuery, State) ->
Escape = #sql_escape{_ = fun(X) -> X end}, Escape = #sql_escape{_ = fun(X) -> X end},
N = length((SQLQuery#sql_query.args)(Escape)), N = length((SQLQuery#sql_query.args)(Escape)),

View File

@ -227,9 +227,11 @@ make_val(Match, Field, Val) ->
Condition = case str:suffix(<<"*">>, Val) of Condition = case str:suffix(<<"*">>, Val) of
true -> true ->
Val1 = str:substr(Val, 1, byte_size(Val) - 1), 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), SVal = ejabberd_sql:escape(Val),
[Field, <<" = '">>, SVal, <<"'">>] [Field, <<" = '">>, SVal, <<"'">>]

View File

@ -51,9 +51,11 @@
get_entity_subscriptions_for_send_last/2, get_last_items/3]). get_entity_subscriptions_for_send_last/2, get_last_items/3]).
-export([decode_jid/1, encode_jid/1, -export([decode_jid/1, encode_jid/1,
encode_jid_like/1,
decode_affiliation/1, decode_subscriptions/1, decode_affiliation/1, decode_subscriptions/1,
encode_affiliation/1, encode_subscriptions/1, encode_affiliation/1, encode_subscriptions/1,
encode_host/1]). encode_host/1,
encode_host_like/1]).
init(_Host, _ServerHost, _Opts) -> init(_Host, _ServerHost, _Opts) ->
%%pubsub_subscription_sql:init(), %%pubsub_subscription_sql:init(),
@ -350,11 +352,13 @@ get_entity_subscriptions(Host, Owner) ->
H = encode_host(Host), H = encode_host(Host),
SJ = encode_jid(SubKey), SJ = encode_jid(SubKey),
GJ = encode_jid(GenKey), GJ = encode_jid(GenKey),
GJLike = encode_jid_like(GenKey),
Query = case SubKey of Query = case SubKey of
GenKey -> GenKey ->
[<<"select node, type, i.nodeid, jid, subscriptions " [<<"select node, type, i.nodeid, jid, subscriptions "
"from pubsub_state i, pubsub_node n " "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 " [<<"select node, type, i.nodeid, jid, subscriptions "
"from pubsub_state i, pubsub_node n " "from pubsub_state i, pubsub_node n "
@ -399,12 +403,14 @@ get_entity_subscriptions_for_send_last(Host, Owner) ->
H = encode_host(Host), H = encode_host(Host),
SJ = encode_jid(SubKey), SJ = encode_jid(SubKey),
GJ = encode_jid(GenKey), GJ = encode_jid(GenKey),
GJLike = encode_jid_like(GenKey),
Query = case SubKey of Query = case SubKey of
GenKey -> GenKey ->
[<<"select node, type, i.nodeid, jid, subscriptions " [<<"select node, type, i.nodeid, jid, subscriptions "
"from pubsub_state i, pubsub_node n, pubsub_node_option o " "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' " "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 " [<<"select node, type, i.nodeid, jid, subscriptions "
"from pubsub_state i, pubsub_node n, pubsub_node_option o " "from pubsub_state i, pubsub_node n, pubsub_node_option o "
@ -568,8 +574,9 @@ get_states(Nidx) ->
[<<"jid">>, <<"affiliation">>, <<"subscriptions">>], RItems} -> [<<"jid">>, <<"affiliation">>, <<"subscriptions">>], RItems} ->
{result, {result,
lists:map(fun ([SJID, Aff, Subs]) -> lists:map(fun ([SJID, Aff, Subs]) ->
#pubsub_state{stateid = {decode_jid(SJID), Nidx}, JID = decode_jid(SJID),
items = itemids(Nidx, SJID), #pubsub_state{stateid = {JID, Nidx},
items = itemids(Nidx, JID),
affiliation = decode_affiliation(Aff), affiliation = decode_affiliation(Aff),
subscriptions = decode_subscriptions(Subs)} subscriptions = decode_subscriptions(Subs)}
end, end,
@ -899,13 +906,12 @@ first_in_list(Pred, [H | T]) ->
_ -> first_in_list(Pred, T) _ -> first_in_list(Pred, T)
end. end.
itemids(Nidx, {U, S, R}) -> itemids(Nidx, {_U, _S, _R} = JID) ->
itemids(Nidx, encode_jid({U, S, R})); SJID = encode_jid_like(JID),
itemids(Nidx, SJID) ->
case catch case catch
ejabberd_sql:sql_query_t([<<"select itemid from pubsub_item where " ejabberd_sql:sql_query_t([<<"select itemid from pubsub_item where "
"nodeid='">>, Nidx, <<"' and publisher like '">>, SJID, "nodeid='">>, Nidx, <<"' and publisher like '">>, SJID,
<<"%' order by modification desc;">>]) <<"%' escape '^' order by modification desc;">>])
of of
{selected, [<<"itemid">>], RItems} -> {selected, [<<"itemid">>], RItems} ->
[ItemId || [ItemId] <- RItems]; [ItemId || [ItemId] <- RItems];
@ -1011,6 +1017,10 @@ decode_subscriptions(Subscriptions) ->
encode_jid(JID) -> encode_jid(JID) ->
ejabberd_sql:escape(jid:to_string(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 :: -spec(encode_host/1 ::
( Host :: host()) ( Host :: host())
-> binary() -> binary()
@ -1018,6 +1028,14 @@ encode_jid(JID) ->
encode_host({_U, _S, _R} = LJID) -> encode_jid(LJID); encode_host({_U, _S, _R} = LJID) -> encode_jid(LJID);
encode_host(Host) -> ejabberd_sql:escape(Host). 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 :: -spec(encode_affiliation/1 ::
( Arg :: atom()) ( Arg :: atom())
-> binary() -> binary()

View File

@ -114,20 +114,21 @@ set_affiliation(Nidx, Owner, Affiliation) ->
get_entity_subscriptions(_Host, Owner) -> get_entity_subscriptions(_Host, Owner) ->
SubKey = jid:tolower(Owner), SubKey = jid:tolower(Owner),
GenKey = jid:remove_resource(SubKey), 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), SJ = node_flat_sql:encode_jid(SubKey),
GJ = node_flat_sql:encode_jid(GenKey), GJ = node_flat_sql:encode_jid(GenKey),
GJLike = node_flat_sql:encode_jid_like(GenKey),
Query = case SubKey of Query = case SubKey of
GenKey -> GenKey ->
[<<"select host, node, type, i.nodeid, jid, " [<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_node n " "subscriptions from pubsub_state i, pubsub_node n "
"where i.nodeid = n.nodeid and jid " "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, " [<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_node n " "subscriptions from pubsub_state i, pubsub_node n "
"where i.nodeid = n.nodeid and jid " "where i.nodeid = n.nodeid and jid "
"in ('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host, <<"';">>] "in ('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, HostLike, <<"' escape '^';">>]
end, end,
Reply = case catch ejabberd_sql:sql_query_t(Query) of Reply = case catch ejabberd_sql:sql_query_t(Query) of
{selected, {selected,
@ -149,9 +150,10 @@ get_entity_subscriptions(_Host, Owner) ->
get_entity_subscriptions_for_send_last(_Host, Owner) -> get_entity_subscriptions_for_send_last(_Host, Owner) ->
SubKey = jid:tolower(Owner), SubKey = jid:tolower(Owner),
GenKey = jid:remove_resource(SubKey), 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), SJ = node_flat_sql:encode_jid(SubKey),
GJ = node_flat_sql:encode_jid(GenKey), GJ = node_flat_sql:encode_jid(GenKey),
GJLike = node_flat_sql:encode_jid_like(GenKey),
Query = case SubKey of Query = case SubKey of
GenKey -> GenKey ->
[<<"select host, node, type, i.nodeid, jid, " [<<"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 " "pubsub_node_option o where i.nodeid = n.nodeid "
"and n.nodeid = o.nodeid and name='send_last_published_item' and " "and n.nodeid = o.nodeid and name='send_last_published_item' and "
"val='on_sub_and_presence' and jid like '">>, "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, " [<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_node n, " "subscriptions from pubsub_state i, pubsub_node n, "
"pubsub_node_option o where i.nodeid = n.nodeid " "pubsub_node_option o where i.nodeid = n.nodeid "
"and n.nodeid = o.nodeid and name='send_last_published_item' and " "and n.nodeid = o.nodeid and name='send_last_published_item' and "
"val='on_sub_and_presence' and jid in ", "val='on_sub_and_presence' and jid in ",
"('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host, <<"';">>] "('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, HostLike, <<"' escape '^';">>]
end, end,
Reply = case catch ejabberd_sql:sql_query_t(Query) of Reply = case catch ejabberd_sql:sql_query_t(Query) of
{selected, {selected,

View File

@ -196,11 +196,11 @@ get_subnodes_tree(Host, Node, _From) ->
get_subnodes_tree(Host, Node) -> get_subnodes_tree(Host, Node) ->
H = node_flat_sql:encode_host(Host), 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 case catch
ejabberd_sql:sql_query_t([<<"select node, parent, type, nodeid from " ejabberd_sql:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>, "pubsub_node where host='">>,
H, <<"' and node like '">>, N, <<"%';">>]) H, <<"' and node like '">>, N, <<"%' escape '^';">>])
of of
{selected, {selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} -> [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} ->
@ -256,10 +256,10 @@ create_node(Host, Node, Type, Owner, Options, Parents) ->
delete_node(Host, Node) -> delete_node(Host, Node) ->
H = node_flat_sql:encode_host(Host), 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), Removed = get_subnodes_tree(Host, Node),
catch ejabberd_sql:sql_query_t([<<"delete from pubsub_node where host='">>, catch ejabberd_sql:sql_query_t([<<"delete from pubsub_node where host='">>,
H, <<"' and node like '">>, N, <<"%';">>]), H, <<"' and node like '">>, N, <<"%' escape '^';">>]),
Removed. Removed.
%% helpers %% helpers

View File

@ -231,12 +231,12 @@ list_users(LServer,
[{prefix, Prefix}, {limit, Limit}, {offset, Offset}]) [{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
when is_binary(Prefix) and is_integer(Limit) and when is_binary(Prefix) and is_integer(Limit) and
is_integer(Offset) -> is_integer(Offset) ->
SPrefix = ejabberd_sql:escape_like_arg(Prefix), SPrefix = ejabberd_sql:escape_like_arg_circumflex(Prefix),
SPrefix2 = <<SPrefix/binary, $%>>, SPrefix2 = <<SPrefix/binary, $%>>,
ejabberd_sql:sql_query( ejabberd_sql:sql_query(
LServer, LServer,
?SQL("select @(username)s from users " ?SQL("select @(username)s from users "
"where username like %(SPrefix2)s " "where username like %(SPrefix2)s escape '^' "
"order by username " "order by username "
"limit %(Limit)d offset %(Offset)d")). "limit %(Limit)d offset %(Offset)d")).
@ -264,12 +264,12 @@ users_number(LServer) ->
users_number(LServer, [{prefix, Prefix}]) users_number(LServer, [{prefix, Prefix}])
when is_binary(Prefix) -> when is_binary(Prefix) ->
SPrefix = ejabberd_sql:escape_like_arg(Prefix), SPrefix = ejabberd_sql:escape_like_arg_circumflex(Prefix),
SPrefix2 = <<SPrefix/binary, $%>>, SPrefix2 = <<SPrefix/binary, $%>>,
ejabberd_sql:sql_query( ejabberd_sql:sql_query(
LServer, LServer,
?SQL("select @(count(*))d from users " ?SQL("select @(count(*))d from users "
"where username like %(SPrefix2)s")); "where username like %(SPrefix2)s escape '^'"));
users_number(LServer, []) -> users_number(LServer, []) ->
users_number(LServer). users_number(LServer).

View File

@ -66,7 +66,7 @@ init_config(Config) ->
{resource, <<"resource">>}, {resource, <<"resource">>},
{master_resource, <<"master_resource">>}, {master_resource, <<"master_resource">>},
{slave_resource, <<"slave_resource">>}, {slave_resource, <<"slave_resource">>},
{password, <<"password">>}, {password, <<"password!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}
{backends, get_config_backends()} {backends, get_config_backends()}
|Config]. |Config].