./ct_report/coverage/mongoose_s2s_dialback.COVER.html

1 %% Steps for S2S Dialback.
2 %% Diagram from https://xmpp.org/extensions/xep-0220.html#intro-howitworks
3 %%
4 %% Initiating Receiving
5 %% Server Server
6 %% ----------- ---------
7 %% | |
8 %% | [if necessary, |
9 %% | perform DNS lookup |
10 %% | on Target Domain, |
11 %% | open TCP connection, |
12 %% | and establish stream] |
13 %% | -----------------------> |
14 %% | | Authoritative
15 %% | send dialback key | Server
16 %% | -------(STEP 1)--------> | -------------
17 %% | | |
18 %% | | [if necessary, |
19 %% | | perform DNS lookup, |
20 %% | | on Sender Domain, |
21 %% | | open TCP connection, |
22 %% | | and establish stream] |
23 %% | | -----------------------> |
24 %% | | |
25 %% | | send verify request |
26 %% | | -------(STEP 2)--------> |
27 %% | | |
28 %% | | send verify response |
29 %% | | <------(STEP 3)--------- |
30 %% | |
31 %% | report dialback result |
32 %% | <-------(STEP 4)-------- |
33 %% | |
34
35 %% Because db:result and db:verify tags are confusing, use step numbers.
36 %% (db:result should've been named db:key).
37
38 -module(mongoose_s2s_dialback).
39 -export([step_1/2,
40 step_2/3,
41 step_3/3,
42 step_4/2]).
43
44 -export([parse_key/1,
45 parse_validity/1]).
46
47 -export([make_key/3]).
48
49 -xep([{xep, 185}, {version, "1.0"}]). %% Dialback Key Generation and Validation
50 -xep([{xep, 220}, {version, "1.1.1"}]). %% Server Dialback
51
52 -include("mongoose.hrl").
53 -include("jlib.hrl").
54
55 %% Initiating server sends dialback key
56 %% https://xmpp.org/extensions/xep-0220.html#example-1
57 -spec step_1(ejabberd_s2s:fromto(), ejabberd_s2s:s2s_dialback_key()) -> exml:element().
58 step_1(FromTo, Key) ->
59 27 #xmlel{name = <<"db:result">>,
60 attrs = fromto_to_attrs(FromTo),
61 children = [#xmlcdata{content = Key}]}.
62
63 %% Receiving server sends verification request to authoritative server (step 2)
64 -spec step_2(ejabberd_s2s:fromto(), ejabberd_s2s:s2s_dialback_key(), ejabberd_s2s:stream_id()) -> exml:element().
65 step_2(FromTo, Key, StreamID) ->
66 27 #xmlel{name = <<"db:verify">>,
67 attrs = [{<<"id">>, StreamID} | fromto_to_attrs(FromTo)],
68 children = [#xmlcdata{content = Key}]}.
69
70 %% Receiving server is informed by authoritative server that key is valid or invalid (step 3)
71 -spec step_3(ejabberd_s2s:fromto(), ejabberd_s2s:stream_id(), boolean()) -> exml:element().
72 step_3(FromTo, StreamID, IsValid) ->
73 26 #xmlel{name = <<"db:verify">>,
74 attrs = [{<<"id">>, StreamID},
75 {<<"type">>, is_valid_to_type(IsValid)}
76 | fromto_to_attrs(FromTo)]}.
77
78 %% Receiving server sends valid or invalid verification result to initiating server (step 4)
79 -spec step_4(ejabberd_s2s:fromto(), boolean()) -> exml:element().
80 step_4(FromTo, IsValid) ->
81 24 #xmlel{name = <<"db:result">>,
82 attrs = [{<<"type">>, is_valid_to_type(IsValid)}
83 | fromto_to_attrs(FromTo)]}.
84
85 -spec fromto_to_attrs(ejabberd_s2s:fromto()) -> [{binary(), binary()}].
86 fromto_to_attrs({LocalServer, RemoteServer}) ->
87 104 [{<<"from">>, LocalServer}, {<<"to">>, RemoteServer}].
88
89 49 is_valid_to_type(true) -> <<"valid">>;
90 1 is_valid_to_type(false) -> <<"invalid">>.
91
92 -spec parse_key(exml:element()) -> false
93 | {Step :: step_1 | step_2,
94 FromTo :: ejabberd_s2s:fromto(),
95 StreamID :: ejabberd_s2s:stream_id(),
96 Key :: ejabberd_s2s:s2s_dialback_key()}.
97 parse_key(El = #xmlel{name = <<"db:result">>}) ->
98 %% Initiating Server Sends Dialback Key (Step 1)
99 26 parse_key(step_1, El);
100 parse_key(El = #xmlel{name = <<"db:verify">>}) ->
101 %% Receiving Server Sends Verification Request to Authoritative Server (Step 2)
102 26 parse_key(step_2, El);
103 parse_key(_) ->
104 80 false.
105
106 parse_key(Step, El) ->
107 52 FromTo = parse_from_to(El),
108 52 StreamID = exml_query:attr(El, <<"id">>, <<>>),
109 52 Key = exml_query:cdata(El),
110 52 {Step, FromTo, StreamID, Key}.
111
112 %% Parse dialback verification result.
113 %% Verification result is stored in the `type' attribute and could be `valid' or `invalid'.
114 -spec parse_validity(exml:element()) -> false
115 | {Step :: step_3 | step_4,
116 FromTo :: ejabberd_s2s:fromto(),
117 StreamID :: ejabberd_s2s:stream_id(),
118 IsValid :: boolean()}.
119 parse_validity(El = #xmlel{name = <<"db:verify">>}) ->
120 %% Receiving Server is Informed by Authoritative Server that Key is Valid or Invalid (Step 3)
121 25 parse_validity(step_3, El);
122 parse_validity(El = #xmlel{name = <<"db:result">>}) ->
123 %% Receiving Server Sends Valid or Invalid Verification Result to Initiating Server (Step 4)
124 24 parse_validity(step_4, El);
125 parse_validity(_) ->
126 1 false.
127
128 parse_validity(Step, El) ->
129 49 FromTo = parse_from_to(El),
130 49 StreamID = exml_query:attr(El, <<"id">>, <<>>),
131 49 IsValid = exml_query:attr(El, <<"type">>) =:= <<"valid">>,
132 49 {Step, FromTo, StreamID, IsValid}.
133
134 -spec parse_from_to(exml:element()) -> ejabberd_s2s:fromto().
135 parse_from_to(El) ->
136 101 RemoteJid = jid:from_binary(exml_query:attr(El, <<"from">>, <<>>)),
137 101 LocalJid = jid:from_binary(exml_query:attr(El, <<"to">>, <<>>)),
138 101 #jid{luser = <<>>, lresource = <<>>, lserver = LRemoteServer} = RemoteJid,
139 101 #jid{luser = <<>>, lresource = <<>>, lserver = LLocalServer} = LocalJid,
140 %% We use fromto() as seen by ejabberd_s2s_out and ejabberd_s2s
141 101 {LLocalServer, LRemoteServer}.
142
143 -spec make_key(ejabberd_s2s:fromto(), ejabberd_s2s:stream_id(), ejabberd_s2s:base16_secret()) ->
144 ejabberd_s2s:s2s_dialback_key().
145 make_key({From, To}, StreamID, Secret) ->
146 53 SecretHashed = base16:encode(crypto:hash(sha256, Secret)),
147 53 HMac = crypto:mac(hmac, sha256, SecretHashed, [From, " ", To, " ", StreamID]),
148 53 base16:encode(HMac).
Line Hits Source