./ct_report/coverage/mongoose_c2s_sasl.COVER.html

1 %% @doc SASL layer between C2S and MIM internal mongoose_acc.
2 %%
3 %% Implements RFC6120 Section 6 – but stanza agnostic, uses mongoose_acc instead
4 %%
5 %% This module is intended to have no side-effects, that is, it proccesses between XMPP contents
6 %% (c2s data, xml stanzas), and MongooseIM internal mongoose_acc,
7 %% which enables tracing by refs and timestamps.
8 %% It does not send anything on the socket nor changes the state of the client process.
9 %% This way, we can decouple SASL from the client process core code.
10 %% @end
11 -module(mongoose_c2s_sasl).
12
13 -include_lib("kernel/include/logger.hrl").
14
15 -export([new/1, start/4, continue/3]).
16
17 -type mechanism() :: binary().
18 -type maybe_username() :: undefined | jid:luser().
19 -type success() :: #{server_out := undefined | binary(),
20 jid := jid:jid(),
21 auth_module := cyrsasl:sasl_module()}.
22 -type continue() :: #{server_out := binary()}.
23 -type failure() :: #{server_out := binary() | {binary(), undefined | iodata()},
24 maybe_username := maybe_username()}.
25 -type error() :: #{type := atom(), text := binary()}.
26 -type result() :: {success, mongoose_acc:t(), success()}
27 | {continue, mongoose_acc:t(), continue()}
28 | {failure, mongoose_acc:t(), failure()}
29 | {error, mongoose_acc:t(), error()}.
30 -export_type([result/0, success/0, continue/0, failure/0, error/0, mechanism/0]).
31
32 -spec new(mongoose_c2s:data()) -> mongoose_acc:t().
33 new(C2SData) ->
34 7070 HostType = mongoose_c2s:get_host_type(C2SData),
35 7070 LServer = mongoose_c2s:get_lserver(C2SData),
36 7070 LOpts = mongoose_c2s:get_listener_opts(C2SData),
37 7070 CredOpts = mongoose_credentials:make_opts(LOpts),
38 7070 Creds = mongoose_credentials:new(LServer, HostType, CredOpts),
39 7070 CyrSaslState = cyrsasl:server_new(<<"jabber">>, LServer, HostType, <<>>, [], Creds),
40 7070 sasl_acc(HostType, LServer, CyrSaslState, Creds).
41
42 -spec start(mongoose_c2s:data(), mongoose_acc:t(), mechanism(), binary()) -> result().
43 start(C2SData, SaslAcc, Mech, ClientIn) ->
44 6689 Socket = mongoose_c2s:get_socket(C2SData),
45 6689 LOpts = mongoose_c2s:get_listener_opts(C2SData),
46 6689 case {mongoose_c2s_socket:is_ssl(Socket), LOpts} of
47 {false, #{tls := #{mode := starttls_required}}} ->
48 4 {error, SaslAcc, #{type => policy_violation, text => <<"Use of STARTTLS required">>}};
49 _ ->
50 6685 AuthMech = mongoose_c2s:get_auth_mechs(C2SData),
51 6685 SocketData = #{socket => Socket, auth_mech => AuthMech, listener_opts => LOpts},
52 6685 CyrSaslState = get_cyrsasl_state_from_acc(SaslAcc),
53 6685 CyrSaslResult = cyrsasl:server_start(CyrSaslState, Mech, ClientIn, SocketData),
54 6685 handle_sasl_step(C2SData, CyrSaslResult, SaslAcc)
55 end.
56
57 -spec continue(mongoose_c2s:data(), mongoose_acc:t(), binary()) -> result().
58 continue(C2SData, SaslAcc, ClientIn) ->
59 48 CyrSaslState = get_cyrsasl_state_from_acc(SaslAcc),
60 48 CyrSaslResult = cyrsasl:server_step(CyrSaslState, ClientIn),
61 48 handle_sasl_step(C2SData, CyrSaslResult, SaslAcc).
62
63 -spec handle_sasl_step(mongoose_c2s:data(), cyrsasl:sasl_result(), mongoose_acc:t()) -> result().
64 handle_sasl_step(C2SData, {ok, Creds}, SaslAcc) ->
65 6612 handle_sasl_success(C2SData, Creds, SaslAcc);
66 handle_sasl_step(C2SData, {continue, ServerOut, NewCyrSaslState}, SaslAcc) ->
67 51 handle_sasl_continue(C2SData, ServerOut, NewCyrSaslState, SaslAcc);
68 handle_sasl_step(C2SData, {error, Error, Username}, SaslAcc) ->
69 21 handle_sasl_failure(C2SData, Error, Username, undefined, SaslAcc);
70 handle_sasl_step(C2SData, {error, Error}, SaslAcc) ->
71 49 MaybeUsername = maybe_get_username_from_data(C2SData),
72 49 handle_sasl_failure(C2SData, Error, undefined, MaybeUsername, SaslAcc).
73
74 -spec handle_sasl_success(mongoose_c2s:data(), mongoose_credentials:t(), mongoose_acc:t()) -> result().
75 handle_sasl_success(C2SData, Creds, SaslAcc) ->
76 6612 ServerOut = mongoose_credentials:get(Creds, sasl_success_response, undefined),
77 6612 AuthModule = mongoose_credentials:get(Creds, auth_module),
78 6612 User = mongoose_credentials:get(Creds, username),
79 6612 LServer = mongoose_c2s:get_lserver(C2SData),
80 6612 Jid = jid:make_bare(User, LServer),
81 6612 Ret = #{server_out => ServerOut, jid => Jid, auth_module => AuthModule},
82 6612 {success, SaslAcc, Ret}.
83
84 -spec handle_sasl_continue(
85 mongoose_c2s:data(), binary(), cyrsasl:sasl_state(), mongoose_acc:t()) -> result().
86 handle_sasl_continue(_C2SData, ServerOut, NewCyrSaslState, SaslAcc) ->
87 51 SaslAcc1 = set_cyrsasl_state_in_acc(SaslAcc, NewCyrSaslState),
88 51 {continue, SaslAcc1, #{server_out => ServerOut}}.
89
90 -spec handle_sasl_failure(
91 mongoose_c2s:data(), term(), maybe_username(), maybe_username(), mongoose_acc:t()) -> result().
92 handle_sasl_failure(_C2SData, Error, undefined, undefined, SaslAcc) ->
93 49 {failure, SaslAcc, #{server_out => Error, maybe_username => undefined}};
94 handle_sasl_failure(_C2SData, Error, Username, _, SaslAcc) ->
95 21 {failure, SaslAcc, #{server_out => Error, maybe_username => Username}}.
96
97 sasl_acc(HostType, LServer, CyrSaslState, Creds) ->
98 7070 Acc = mongoose_acc:new(#{host_type => HostType, lserver => LServer, location => ?LOCATION}),
99 7070 Fields = [{creds, Creds}, {cyrsasl, CyrSaslState}],
100 7070 mongoose_acc:set(?MODULE, Fields, Acc).
101
102 -spec set_cyrsasl_state_in_acc(mongoose_acc:t(), cyrsasl:sasl_state()) -> mongoose_acc:t().
103 set_cyrsasl_state_in_acc(SaslAcc, NewCyrSaslState) ->
104 51 mongoose_acc:set(?MODULE, cyrsasl, NewCyrSaslState, SaslAcc).
105
106 -spec get_cyrsasl_state_from_acc(mongoose_acc:t()) -> cyrsasl:sasl_state().
107 get_cyrsasl_state_from_acc(SaslAcc) ->
108 6733 mongoose_acc:get(?MODULE, cyrsasl, SaslAcc).
109
110 -spec maybe_get_username_from_data(mongoose_c2s:data()) -> maybe_username().
111 maybe_get_username_from_data(C2SData) ->
112 49 case mongoose_c2s:get_jid(C2SData) of
113 undefined ->
114 49 undefined;
115 Jid ->
116
:-(
{U, _S} = jid:to_lus(Jid),
117
:-(
U
118 end.
Line Hits Source