]> granicus.if.org Git - ejabberd/commitdiff
Update SQL escaping
authorAlexey Shchepin <alexey@process-one.net>
Thu, 12 May 2016 15:32:13 +0000 (18:32 +0300)
committerAlexey Shchepin <alexey@process-one.net>
Fri, 13 May 2016 14:56:48 +0000 (17:56 +0300)
src/ejabberd_sql.erl
src/mod_vcard_sql.erl
src/node_flat_sql.erl
src/node_pep_sql.erl
src/nodetree_tree_sql.erl
src/sql_queries.erl
test/suite.erl

index 09bd3197390414a8e1bed84110d6f9f753c1280b..f503a749f6a477e204c59ed20cd9cd946ffe53a2 100644 (file)
@@ -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)),
index 6b8e903338cfb459717afa90833c24d683d2cd58..b8234bf9c699d2938890e0e8d224e1fd609db2c4 100644 (file)
@@ -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, <<"'">>]
index 5baabcfcfaad3f08ad9e2b615ddceb251f3df6a3..ea3aa7d3cbd42f92261011b51acfdaeb8899911c 100644 (file)
     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()
index c5b31d15fab55f8179049bcc640642496740cae2..1f2c13d5c4ea8d1600ef158ef00fb3ce9de7c832 100644 (file)
@@ -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,
index b56543395a4cae3d37bf9bd8d1db144a71af0add..17ae91b52699d8690ef61e6b52617ccd03e76ead 100644 (file)
@@ -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
index e1374a817382b2666bd4ec01cfee5c6e90c60964..121117574d71c5a5dd1d97056169e623fa384615 100644 (file)
@@ -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).
 
index f37b51ea6259585483a8e4d0aa486cf6ac952b7b..fb8b6ae2f94144068a663ab165dd17d7719b8b5c 100644 (file)
@@ -66,7 +66,7 @@ init_config(Config) ->
      {resource, <<"resource">>},
      {master_resource, <<"master_resource">>},
      {slave_resource, <<"slave_resource">>},
-     {password, <<"password">>},
+     {password, <<"password!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}
      {backends, get_config_backends()}
      |Config].