22
22
socket_ready /2 ,
23
23
protocol_header_received /5 ,
24
24
begin_session /1 ,
25
- heartbeat /1 ,
26
- encrypt_sasl /1 ,
27
- decrypt_sasl /1 ]).
25
+ heartbeat /1 ]).
28
26
29
27
% % gen_statem callbacks
30
28
-export ([init /1 ,
52
50
-type address () :: inet :socket_address () | inet :hostname ().
53
51
54
52
-type encrypted_sasl () :: {plaintext , binary ()} | {encrypted , binary ()}.
55
- -type decrypted_sasl () :: none | anon | {plain , User :: binary (), Pwd :: binary ()}.
53
+ -type decrypted_sasl () :: none | anon | external | {plain , User :: binary (), Pwd :: binary ()}.
54
+ -type sasl () :: encrypted_sasl () | decrypted_sasl ().
56
55
57
56
-type connection_config () ::
58
57
#{container_id => binary (), % AMQP container id
72
71
% set to a negative value to allow a sender to "overshoot" the flow
73
72
% control by this margin
74
73
transfer_limit_margin => 0 | neg_integer (),
75
- % % These credentials_obfuscation-wrapped values have the type of
76
- % % decrypted_sasl/0
77
- sasl => encrypted_sasl () | decrypted_sasl (),
74
+ sasl => sasl (),
78
75
properties => amqp10_client_types :properties ()
79
76
}.
80
77
92
89
}).
93
90
94
91
-export_type ([connection_config / 0 ,
95
- amqp10_socket / 0 ,
96
- encrypted_sasl / 0 ,
97
- decrypted_sasl / 0 ]).
92
+ amqp10_socket / 0 ]).
98
93
99
94
% % -------------------------------------------------------------------
100
95
% % Public API.
101
96
% % -------------------------------------------------------------------
102
97
103
98
-spec open (connection_config ()) -> supervisor :startchild_ret ().
104
- open (Config ) ->
99
+ open (Config0 ) ->
100
+ Config = maps :update_with (sasl , fun maybe_encrypt_sasl /1 , Config0 ),
105
101
% % Start the supervision tree dedicated to that connection. It
106
102
% % starts at least a connection process (the PID we want to return)
107
103
% % and a reader process (responsible for opening and reading the
@@ -127,17 +123,23 @@ open(Config) ->
127
123
close (Pid , Reason ) ->
128
124
gen_statem :cast (Pid , {close , Reason }).
129
125
130
- -spec encrypt_sasl (decrypted_sasl ()) -> encrypted_sasl ().
131
- encrypt_sasl (none ) ->
132
- credentials_obfuscation :encrypt (none );
133
- encrypt_sasl (DecryptedSasl ) ->
134
- credentials_obfuscation :encrypt (term_to_binary (DecryptedSasl )).
135
-
136
- -spec decrypt_sasl (encrypted_sasl ()) -> decrypted_sasl ().
137
- decrypt_sasl (none ) ->
138
- credentials_obfuscation :decrypt (none );
139
- decrypt_sasl (EncryptedSasl ) ->
140
- binary_to_term (credentials_obfuscation :decrypt (EncryptedSasl )).
126
+ -spec maybe_encrypt_sasl (decrypted_sasl ()) -> sasl ().
127
+ maybe_encrypt_sasl (Sasl )
128
+ when Sasl =:= none orelse
129
+ Sasl =:= anon orelse
130
+ Sasl =:= external ->
131
+ Sasl ;
132
+ maybe_encrypt_sasl (Plain = {plain , _User , _Passwd }) ->
133
+ credentials_obfuscation :encrypt (term_to_binary (Plain )).
134
+
135
+ -spec maybe_decrypt_sasl (sasl ()) -> decrypted_sasl ().
136
+ maybe_decrypt_sasl (Sasl )
137
+ when Sasl =:= none orelse
138
+ Sasl =:= anon orelse
139
+ Sasl =:= external ->
140
+ Sasl ;
141
+ maybe_decrypt_sasl (Encrypted ) ->
142
+ binary_to_term (credentials_obfuscation :decrypt (Encrypted )).
141
143
142
144
% % -------------------------------------------------------------------
143
145
% % Private API.
@@ -207,13 +209,11 @@ sasl_hdr_sent({call, From}, begin_session,
207
209
{keep_state , State1 }.
208
210
209
211
sasl_hdr_rcvds (_EvtType , # 'v1_0.sasl_mechanisms' {
210
- sasl_server_mechanisms = {array , symbol , Mechs }},
211
- State = # state {config = #{sasl := EncryptedSasl }}) ->
212
- DecryptedSasl = decrypt_sasl (EncryptedSasl ),
213
- SaslBin = {symbol , decrypted_sasl_to_bin (DecryptedSasl )},
214
- case lists :any (fun (S ) when S =:= SaslBin -> true ;
215
- (_ ) -> false
216
- end , Mechs ) of
212
+ sasl_server_mechanisms = {array , symbol , AvailableMechs }},
213
+ State = # state {config = #{sasl := Sasl }}) ->
214
+ DecryptedSasl = maybe_decrypt_sasl (Sasl ),
215
+ OurMech = {symbol , decrypted_sasl_to_mechanism (DecryptedSasl )},
216
+ case lists :member (OurMech , AvailableMechs ) of
217
217
true ->
218
218
ok = send_sasl_init (State , DecryptedSasl ),
219
219
{next_state , sasl_init_sent , State };
@@ -454,6 +454,15 @@ send_close(#state{socket = Socket}, _Reason) ->
454
454
send_sasl_init (State , anon ) ->
455
455
Frame = # 'v1_0.sasl_init' {mechanism = {symbol , <<" ANONYMOUS" >>}},
456
456
send (Frame , 1 , State );
457
+ send_sasl_init (State , external ) ->
458
+ Frame = # 'v1_0.sasl_init' {
459
+ mechanism = {symbol , <<" EXTERNAL" >>},
460
+ % % "This response is empty when the client is requesting to act
461
+ % % as the identity the server associated with its authentication
462
+ % % credentials."
463
+ % % https://datatracker.ietf.org/doc/html/rfc4422#appendix-A.1
464
+ initial_response = {binary , <<>>}},
465
+ send (Frame , 1 , State );
457
466
send_sasl_init (State , {plain , User , Pass }) ->
458
467
Response = <<0 :8 , User /binary , 0 :8 , Pass /binary >>,
459
468
Frame = # 'v1_0.sasl_init' {mechanism = {symbol , <<" PLAIN" >>},
@@ -546,9 +555,12 @@ translate_err(#'v1_0.error'{condition = Cond, description = Desc}) ->
546
555
amqp10_event (Evt ) ->
547
556
{amqp10_event , {connection , self (), Evt }}.
548
557
549
- decrypted_sasl_to_bin ({plain , _ , _ }) -> <<" PLAIN" >>;
550
- decrypted_sasl_to_bin (anon ) -> <<" ANONYMOUS" >>;
551
- decrypted_sasl_to_bin (none ) -> <<" ANONYMOUS" >>.
558
+ decrypted_sasl_to_mechanism (anon ) ->
559
+ <<" ANONYMOUS" >>;
560
+ decrypted_sasl_to_mechanism (external ) ->
561
+ <<" EXTERNAL" >>;
562
+ decrypted_sasl_to_mechanism ({plain , _ , _ }) ->
563
+ <<" PLAIN" >>.
552
564
553
565
config_defaults () ->
554
566
#{sasl => none ,
0 commit comments