Skip to content

Commit dd37261

Browse files
committed
Add missing exchange write access check
when an MQTT will message is published.
1 parent ea57cb6 commit dd37261

File tree

3 files changed

+87
-58
lines changed

3 files changed

+87
-58
lines changed

deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl

+40-50
Original file line numberDiff line numberDiff line change
@@ -255,39 +255,24 @@ process_request(?PUBLISH,
255255
packet_id = PacketId },
256256
payload = Payload},
257257
State0 = #state{unacked_client_pubs = U,
258-
cfg = #cfg{retainer_pid = RPid,
259-
proto_ver = ProtoVer}}) ->
258+
cfg = #cfg{proto_ver = ProtoVer}}) ->
260259
EffectiveQos = maybe_downgrade_qos(Qos),
261260
rabbit_global_counters:messages_received(ProtoVer, 1),
262261
State = maybe_increment_publisher(State0),
263-
Publish = fun() ->
264-
Msg = #mqtt_msg{retain = Retain,
265-
qos = EffectiveQos,
266-
topic = Topic,
267-
dup = Dup,
268-
packet_id = PacketId,
269-
payload = Payload},
270-
case publish_to_queues(Msg, State) of
271-
{ok, _} = Ok ->
272-
case Retain of
273-
false ->
274-
ok;
275-
true ->
276-
hand_off_to_retainer(RPid, Topic, Msg)
277-
end,
278-
Ok;
279-
Error ->
280-
Error
281-
end
282-
end,
262+
Msg = #mqtt_msg{retain = Retain,
263+
qos = EffectiveQos,
264+
topic = Topic,
265+
dup = Dup,
266+
packet_id = PacketId,
267+
payload = Payload},
283268
case EffectiveQos of
284269
?QOS_0 ->
285-
publish_to_queues_with_checks(Topic, Publish, State);
270+
publish_to_queues_with_checks(Msg, State);
286271
?QOS_1 ->
287272
rabbit_global_counters:messages_received_confirm(ProtoVer, 1),
288273
case rabbit_mqtt_confirms:contains(PacketId, U) of
289274
false ->
290-
publish_to_queues_with_checks(Topic, Publish, State);
275+
publish_to_queues_with_checks(Msg, State);
291276
true ->
292277
%% Client re-sent this PUBLISH packet.
293278
%% We already sent this message to target queues awaiting confirmations.
@@ -1226,25 +1211,16 @@ terminate(SendWill, ConnName, ProtoFamily,
12261211
maybe_decrement_publisher(State),
12271212
maybe_delete_mqtt_qos0_queue(State).
12281213

1229-
maybe_send_will(
1230-
true, ConnStr,
1231-
#state{cfg = #cfg{retainer_pid = RPid,
1232-
will_msg = WillMsg = #mqtt_msg{retain = Retain,
1233-
topic = Topic}}
1234-
} = State) ->
1235-
?LOG_DEBUG("sending MQTT will message to topic ~s on connection ~s",
1236-
[Topic, ConnStr]),
1237-
case check_topic_access(Topic, write, State) of
1238-
ok ->
1239-
_ = publish_to_queues(WillMsg, State),
1240-
case Retain of
1241-
false ->
1242-
ok;
1243-
true ->
1244-
hand_off_to_retainer(RPid, Topic, WillMsg)
1245-
end;
1246-
{error, access_refused = Reason} ->
1247-
?LOG_ERROR("failed to send will message: ~p", [Reason])
1214+
-spec maybe_send_will(boolean(), binary(), state()) -> ok.
1215+
maybe_send_will(true, ConnStr,
1216+
State = #state{cfg = #cfg{will_msg = WillMsg = #mqtt_msg{topic = Topic}}}) ->
1217+
case publish_to_queues_with_checks(WillMsg, State) of
1218+
{ok, _} ->
1219+
?LOG_DEBUG("sent MQTT will message to topic ~s on connection ~s",
1220+
[Topic, ConnStr]);
1221+
{error, Reason, _} ->
1222+
?LOG_DEBUG("failed to send MQTT will message to topic ~s on connection ~s: ~p",
1223+
[Topic, ConnStr, Reason])
12481224
end;
12491225
maybe_send_will(_, _, _) ->
12501226
ok.
@@ -1546,17 +1522,31 @@ trace_tap_out(Msg0 = {?QUEUE_TYPE_QOS_0, _, _, _, _},
15461522
trace_tap_out(Msg, State)
15471523
end.
15481524

1525+
-spec publish_to_queues_with_checks(mqtt_msg(), state()) ->
1526+
{ok, state()} | {error, any(), state()}.
15491527
publish_to_queues_with_checks(
1550-
TopicName, PublishFun,
1551-
#state{cfg = #cfg{exchange = Exchange},
1552-
auth_state = #auth_state{user = User,
1553-
authz_ctx = AuthzCtx}
1554-
} = State) ->
1528+
Msg = #mqtt_msg{topic = Topic,
1529+
retain = Retain},
1530+
State = #state{cfg = #cfg{exchange = Exchange,
1531+
retainer_pid = RPid},
1532+
auth_state = #auth_state{user = User,
1533+
authz_ctx = AuthzCtx}}) ->
15551534
case check_resource_access(User, Exchange, write, AuthzCtx) of
15561535
ok ->
1557-
case check_topic_access(TopicName, write, State) of
1536+
case check_topic_access(Topic, write, State) of
15581537
ok ->
1559-
PublishFun();
1538+
case publish_to_queues(Msg, State) of
1539+
{ok, _} = Ok ->
1540+
case Retain of
1541+
false ->
1542+
ok;
1543+
true ->
1544+
hand_off_to_retainer(RPid, Topic, Msg)
1545+
end,
1546+
Ok;
1547+
Error ->
1548+
Error
1549+
end;
15601550
{error, access_refused} ->
15611551
{error, unauthorized, State}
15621552
end;

deps/rabbitmq_mqtt/test/auth_SUITE.erl

+39
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ groups() ->
7777
no_queue_delete_permission,
7878
no_queue_declare_permission,
7979
no_publish_permission,
80+
no_publish_permission_will_message,
8081
no_topic_read_permission,
8182
no_topic_write_permission,
8283
topic_write_permission_variable_expansion,
@@ -339,6 +340,7 @@ end_per_testcase(Testcase, Config) when Testcase == no_queue_bind_permission;
339340
Testcase == no_queue_delete_permission;
340341
Testcase == no_queue_declare_permission;
341342
Testcase == no_publish_permission;
343+
Testcase == no_publish_permission_will_message;
342344
Testcase == no_topic_read_permission;
343345
Testcase == no_topic_write_permission;
344346
Testcase == topic_write_permission_variable_expansion;
@@ -699,6 +701,43 @@ no_publish_permission(Config) ->
699701
]),
700702
ok.
701703

704+
%% Test that publish permission checks are performed for the will message.
705+
no_publish_permission_will_message(Config) ->
706+
%% Allow write access to queue.
707+
%% Disallow write access to exchange.
708+
set_permissions(".*", "^mqtt-subscription.*qos1$", ".*", Config),
709+
Topic = <<"will/topic">>,
710+
Opts = [{will_topic, Topic},
711+
{will_payload, <<"will payload">>},
712+
{will_qos, 0}],
713+
{ok, C} = connect_user(?config(mqtt_user, Config),
714+
?config(mqtt_password, Config),
715+
Config,
716+
<<"client-with-will">>,
717+
Opts),
718+
{ok, _} = emqtt:connect(C),
719+
timer:sleep(100),
720+
[ServerPublisherPid] = util:all_connection_pids(Config),
721+
722+
Sub = open_mqtt_connection(Config),
723+
{ok, _, [1]} = emqtt:subscribe(Sub, Topic, qos1),
724+
725+
unlink(C),
726+
%% Trigger sending of will message.
727+
erlang:exit(ServerPublisherPid, test_will),
728+
729+
%% We expect to not receive a will message because of missing publish permission.
730+
receive Unexpected -> ct:fail("Received unexpectedly: ~p", [Unexpected])
731+
after 300 -> ok
732+
end,
733+
734+
wait_log(Config,
735+
[{["MQTT resource access refused: write access to exchange "
736+
"'amq.topic' in vhost 'mqtt-vhost' refused for user 'mqtt-user'"],
737+
fun () -> stop end}
738+
]),
739+
ok = emqtt:disconnect(Sub).
740+
702741
no_topic_read_permission(Config) ->
703742
set_permissions(".*", ".*", ".*", Config),
704743
set_topic_permissions("^allow-write\\..*", "^allow-read\\..*", Config),

deps/rabbitmq_mqtt/test/shared_SUITE.erl

+8-8
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,10 @@ pubsub_separate_connections(Config) ->
243243
will_with_disconnect(Config) ->
244244
LastWillTopic = <<"/topic/last-will">>,
245245
LastWillMsg = <<"last will message">>,
246-
PubOpts = [{will_topic, LastWillTopic},
247-
{will_payload, LastWillMsg},
248-
{will_qos, 1}],
249-
Pub = connect(<<(atom_to_binary(?FUNCTION_NAME))/binary, "_publisher">>, Config, PubOpts),
246+
Opts = [{will_topic, LastWillTopic},
247+
{will_payload, LastWillMsg},
248+
{will_qos, 1}],
249+
Pub = connect(<<(atom_to_binary(?FUNCTION_NAME))/binary, "_publisher">>, Config, Opts),
250250
Sub = connect(<<(atom_to_binary(?FUNCTION_NAME))/binary, "_subscriber">>, Config),
251251
{ok, _, [1]} = emqtt:subscribe(Sub, LastWillTopic, qos1),
252252

@@ -260,10 +260,10 @@ will_with_disconnect(Config) ->
260260
will_without_disconnect(Config) ->
261261
LastWillTopic = <<"/topic/last-will">>,
262262
LastWillMsg = <<"last will message">>,
263-
PubOpts = [{will_topic, LastWillTopic},
264-
{will_payload, LastWillMsg},
265-
{will_qos, 1}],
266-
Pub = connect(<<(atom_to_binary(?FUNCTION_NAME))/binary, "_publisher">>, Config, PubOpts),
263+
Opts = [{will_topic, LastWillTopic},
264+
{will_payload, LastWillMsg},
265+
{will_qos, 1}],
266+
Pub = connect(<<(atom_to_binary(?FUNCTION_NAME))/binary, "_publisher">>, Config, Opts),
267267
timer:sleep(100),
268268
[ServerPublisherPid] = all_connection_pids(Config),
269269
Sub = connect(<<(atom_to_binary(?FUNCTION_NAME))/binary, "_subscriber">>, Config),

0 commit comments

Comments
 (0)