Skip to content

Commit 9f2e4bf

Browse files
dependent on simple authentication and internal config, the client password is now cleared or kept. (#609)
* dependent on simple authentication and internal config, the client password is now cleared or kept. * add conditional import for hivemq-extension-sdk * corrected path for extension sdk --------- Co-authored-by: Daniel Krueger <[email protected]>
1 parent 9ee9ce5 commit 9f2e4bf

File tree

8 files changed

+122
-1
lines changed

8 files changed

+122
-1
lines changed

hivemq-edge/settings.gradle.kts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ includeBuild("./src/frontend") {
2525
name = "hivemq-edge-frontend"
2626
}
2727

28+
if (file("../../hivemq-extension-sdk").exists()) {
29+
includeBuild("../../hivemq-extension-sdk")
30+
} else {
31+
logger.warn(
32+
"""
33+
######################################################################################################
34+
You can not use the latest changes of or modify the hivemq-extension-sdk.
35+
Please checkout the hivemq-extension-sdk repository next to the hivemq-community-edition repository.
36+
Execute the following command from your project directory:
37+
git clone https://github.com/hivemq/hivemq-extension-sdk.git ../hivemq-extension-sdk
38+
You can also clone your fork:
39+
git clone https://github.com/<replace-with-your-fork>/hivemq-extension-sdk.git ../hivemq-extension-sdk
40+
######################################################################################################
41+
""".trimIndent()
42+
)
43+
}
44+
2845
if (file("../../hivemq-edge-extension-sdk").exists()) {
2946
includeBuild("../../hivemq-edge-extension-sdk")
3047
} else {

hivemq-edge/src/main/java/com/hivemq/bootstrap/ClientConnection.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.net.InetSocketAddress;
4242
import java.net.SocketAddress;
4343
import java.nio.ByteBuffer;
44+
import java.util.Arrays;
4445
import java.util.HashMap;
4546
import java.util.Optional;
4647
import java.util.concurrent.ScheduledFuture;
@@ -97,6 +98,7 @@ public class ClientConnection {
9798
private @Nullable ByteBuffer authData;
9899
private @Nullable Mqtt5UserProperties authUserProperties;
99100
private @Nullable ScheduledFuture<?> authFuture;
101+
private @Nullable Boolean clearPasswordAfterAuth;
100102

101103
private @Nullable ClientContextImpl extensionClientContext;
102104
private @Nullable ClientEventListeners extensionClientEventListeners;
@@ -581,6 +583,22 @@ public int getMaxInflightWindow(final int defaultMaxInflightWindow) {
581583
return Optional.empty();
582584
}
583585

586+
public void setClearPasswordAfterAuth(final @Nullable Boolean clearPasswordAfterAuth) {
587+
this.clearPasswordAfterAuth = clearPasswordAfterAuth;
588+
}
589+
590+
public @NotNull Optional<Boolean> isClearPasswordAfterAuth() {
591+
return Optional.ofNullable(clearPasswordAfterAuth);
592+
}
593+
594+
public void clearPassword(){
595+
if(authPassword == null) {
596+
return;
597+
}
598+
Arrays.fill(authPassword, (byte) 0);
599+
authPassword = null;
600+
}
601+
584602
public @NotNull HashMap<String, Object> getAdditionalInformation() {
585603
return additionalInformation;
586604
}

hivemq-edge/src/main/java/com/hivemq/configuration/service/InternalConfigurations.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ public class InternalConfigurations {
251251
public static final int NETTY_COUNT_OF_CONNECTIONS_IN_SHUTDOWN_PARTITION = 100;
252252

253253
public static final double MQTT_CONNECTION_KEEP_ALIVE_FACTOR = 1.5;
254+
public static final boolean MQTT_CONNECTION_AUTH_CLEAR_PASSWORD = true;
254255

255256
public static final long DISCONNECT_KEEP_ALIVE_BATCH = 100;
256257

hivemq-edge/src/main/java/com/hivemq/extensions/auth/ConnectAuthContext.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public ConnectAuthContext(
6363
void succeedAuthentication(final @NotNull ConnectAuthOutput output) {
6464
super.succeedAuthentication(output);
6565
final ClientConnection clientConnection = ctx.channel().attr(ClientConnection.CHANNEL_ATTRIBUTE_NAME).get();
66+
clientConnection.setClearPasswordAfterAuth(output.isClearPasswordAfterAuth());
6667
clientConnection.setAuthData(output.getAuthenticationData());
6768
clientConnection.setAuthUserProperties(Mqtt5UserProperties.of(output.getOutboundUserProperties().asInternalList()));
6869
connectHandler.connectSuccessfulAuthenticated(ctx, clientConnection, connect, output.getClientSettings());

hivemq-edge/src/main/java/com/hivemq/extensions/auth/ConnectAuthOutput.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public class ConnectAuthOutput extends AuthOutput<EnhancedAuthOutput> implements
4242

4343
private @NotNull Mqtt5ConnAckReasonCode reasonCode = Mqtt5ConnAckReasonCode.NOT_AUTHORIZED;
4444
private @NotNull Mqtt5ConnAckReasonCode timeoutReasonCode = Mqtt5ConnAckReasonCode.NOT_AUTHORIZED;
45+
private @Nullable Boolean clearPasswordAfterAuth;
4546
private final boolean supportsEnhancedAuth;
4647

4748
public ConnectAuthOutput(
@@ -68,6 +69,11 @@ private void setDefaultReasonStrings() {
6869
timeoutReasonString = ReasonStrings.AUTH_FAILED_EXTENSION_TIMEOUT;
6970
}
7071

72+
public void authenticateSuccessfully(final boolean clearPasswordAfterAuth) {
73+
this.clearPasswordAfterAuth = clearPasswordAfterAuth;
74+
super.authenticateSuccessfully();
75+
}
76+
7177
@Override
7278
public void authenticateSuccessfully(final @NotNull ByteBuffer authenticationData) {
7379
checkEnhancedAuthSupport();
@@ -194,6 +200,10 @@ void failByThrowable(final @NotNull Throwable throwable) {
194200
return reasonCode;
195201
}
196202

203+
public @Nullable Boolean isClearPasswordAfterAuth() {
204+
return clearPasswordAfterAuth;
205+
}
206+
197207
private static @NotNull Mqtt5ConnAckReasonCode checkReasonCode(final @NotNull ConnackReasonCode reasonCode) {
198208
checkNotNull(reasonCode, "CONNACK reason code must never be null");
199209
checkArgument(

hivemq-edge/src/main/java/com/hivemq/extensions/auth/ConnectSimpleAuthOutput.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ public void authenticateSuccessfully() {
4343
delegate.authenticateSuccessfully();
4444
}
4545

46+
@Override
47+
public void authenticateSuccessfully(final boolean clearPasswordAfterAuth) {
48+
delegate.authenticateSuccessfully(clearPasswordAfterAuth);
49+
}
50+
4651
@Override
4752
public void failAuthentication() {
4853
delegate.failAuthentication();
@@ -150,4 +155,4 @@ public T getOutput() {
150155
return delegate.getStatus();
151156
}
152157
}
153-
}
158+
}

hivemq-edge/src/main/java/com/hivemq/mqtt/handler/connect/ConnectHandler.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import javax.inject.Singleton;
7676
import java.nio.ByteBuffer;
7777
import java.util.NoSuchElementException;
78+
import java.util.Optional;
7879
import java.util.concurrent.TimeUnit;
7980

8081
import static com.hivemq.bootstrap.netty.ChannelHandlerNames.AUTH_IN_PROGRESS_MESSAGE_HANDLER;
@@ -84,6 +85,7 @@
8485
import static com.hivemq.bootstrap.netty.ChannelHandlerNames.MQTT_MESSAGE_BARRIER;
8586
import static com.hivemq.bootstrap.netty.ChannelHandlerNames.MQTT_PUBLISH_FLOW_HANDLER;
8687
import static com.hivemq.configuration.service.InternalConfigurations.AUTH_DENY_UNAUTHENTICATED_CONNECTIONS;
88+
import static com.hivemq.configuration.service.InternalConfigurations.MQTT_CONNECTION_AUTH_CLEAR_PASSWORD;
8789
import static com.hivemq.mqtt.message.connack.CONNACK.KEEP_ALIVE_NOT_SET;
8890
import static com.hivemq.mqtt.message.connack.Mqtt5CONNACK.DEFAULT_MAXIMUM_PACKET_SIZE_NO_LIMIT;
8991

@@ -210,6 +212,7 @@ public void connectSuccessfulUndecided(
210212
final @NotNull CONNECT connect,
211213
final @Nullable ModifiableClientSettingsImpl clientSettings) {
212214

215+
clearPasswordIfWanted(clientConnection);
213216
if (AUTH_DENY_UNAUTHENTICATED_CONNECTIONS.get()) {
214217
mqttConnacker.connackError(
215218
clientConnection.getChannel(),
@@ -233,11 +236,25 @@ public void connectSuccessfulAuthenticated(
233236
final @NotNull CONNECT connect,
234237
final @Nullable ModifiableClientSettingsImpl clientSettings) {
235238

239+
clearPasswordIfWanted(clientConnection);
236240
clientConnection.proposeClientState(ClientState.AUTHENTICATED);
237241
connectAuthenticated(ctx, clientConnection, connect, clientSettings);
238242
cleanChannelAttributesAfterAuth(clientConnection);
239243
}
240244

245+
private void clearPasswordIfWanted(final @NotNull ClientConnection clientConnection) {
246+
final Optional<Boolean> clearPasswordAfterAuthOptional = clientConnection.isClearPasswordAfterAuth();
247+
if(clearPasswordAfterAuthOptional.isPresent()) {
248+
if(clearPasswordAfterAuthOptional.get()) {
249+
clientConnection.clearPassword();
250+
}
251+
} else {
252+
if(MQTT_CONNECTION_AUTH_CLEAR_PASSWORD) {
253+
clientConnection.clearPassword();
254+
}
255+
}
256+
}
257+
241258
private static void cleanChannelAttributesAfterAuth(final @NotNull ClientConnection clientConnection) {
242259
final ChannelPipeline pipeline = clientConnection.getChannel().pipeline();
243260
if (pipeline.context(AUTH_IN_PROGRESS_MESSAGE_HANDLER) != null) {

hivemq-edge/src/test/java/com/hivemq/mqtt/handler/connect/ConnectHandlerTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,14 @@
8787
import util.*;
8888

8989
import javax.inject.Provider;
90+
import java.nio.charset.StandardCharsets;
9091
import java.util.Map;
9192
import java.util.concurrent.CountDownLatch;
9293
import java.util.concurrent.TimeUnit;
9394
import java.util.concurrent.atomic.AtomicLong;
9495
import java.util.concurrent.atomic.AtomicReference;
9596

97+
import static com.hivemq.configuration.service.InternalConfigurations.MQTT_CONNECTION_AUTH_CLEAR_PASSWORD;
9698
import static com.hivemq.extension.sdk.api.auth.parameter.OverloadProtectionThrottlingLevel.NONE;
9799
import static com.hivemq.mqtt.message.connack.CONNACK.KEEP_ALIVE_NOT_SET;
98100
import static com.hivemq.mqtt.message.connect.Mqtt5CONNECT.SESSION_EXPIRY_MAX;
@@ -1481,6 +1483,56 @@ public void test_set_client_settings() {
14811483
assertEquals(123, connect.getReceiveMaximum());
14821484
}
14831485

1486+
@Test(timeout = 5000)
1487+
public void test_contextWantsPasswordErasure_passwordCleared() {
1488+
createHandler();
1489+
final CONNECT connect = new CONNECT.Mqtt5Builder().withClientIdentifier("client").build();
1490+
1491+
final ClientConnection clientConnectionContext = channel.attr(ClientConnection.CHANNEL_ATTRIBUTE_NAME).get();
1492+
clientConnectionContext.setClearPasswordAfterAuth(true);
1493+
clientConnectionContext.setAuthPassword("password".getBytes(StandardCharsets.UTF_8));
1494+
1495+
final ModifiableClientSettingsImpl clientSettings = new ModifiableClientSettingsImpl(65535, null);
1496+
handler.connectSuccessfulAuthenticated(ctx, clientConnectionContext, connect, clientSettings);
1497+
1498+
assertNull(clientConnectionContext.getAuthPassword());
1499+
}
1500+
1501+
@Test(timeout = 5000)
1502+
public void test_contextDoesNotWantPasswordErasure_passwordKept() {
1503+
createHandler();
1504+
final CONNECT connect = new CONNECT.Mqtt5Builder().withClientIdentifier("client").build();
1505+
1506+
final ClientConnection clientConnectionContext = channel.attr(ClientConnection.CHANNEL_ATTRIBUTE_NAME).get();
1507+
clientConnectionContext.setClearPasswordAfterAuth(false);
1508+
clientConnectionContext.setAuthPassword("password".getBytes(StandardCharsets.UTF_8));
1509+
1510+
final ModifiableClientSettingsImpl clientSettings = new ModifiableClientSettingsImpl(65535, null);
1511+
handler.connectSuccessfulAuthenticated(ctx, clientConnectionContext, connect, clientSettings);
1512+
1513+
assertNotNull(clientConnectionContext.getAuthPassword());
1514+
}
1515+
1516+
@Test(timeout = 5000)
1517+
public void test_contextDoesNotDecidePasswordErasure_passwordKeptOrCleared() {
1518+
createHandler();
1519+
final CONNECT connect =
1520+
new CONNECT.Mqtt5Builder().withClientIdentifier("client").build();
1521+
1522+
final ClientConnection clientConnectionContext = channel.attr(ClientConnection.CHANNEL_ATTRIBUTE_NAME).get();
1523+
clientConnectionContext.setClearPasswordAfterAuth(null);
1524+
clientConnectionContext.setAuthPassword("password".getBytes(StandardCharsets.UTF_8));
1525+
1526+
final ModifiableClientSettingsImpl clientSettings = new ModifiableClientSettingsImpl(65535, null);
1527+
handler.connectSuccessfulAuthenticated(ctx, clientConnectionContext, connect, clientSettings);
1528+
1529+
if (MQTT_CONNECTION_AUTH_CLEAR_PASSWORD) {
1530+
assertNull(clientConnectionContext.getAuthPassword());
1531+
} else {
1532+
assertNotNull(clientConnectionContext.getAuthPassword());
1533+
}
1534+
}
1535+
14841536
@Test
14851537
public void test_start_connection_persistent() throws Exception {
14861538
final CONNECT connect = new CONNECT.Mqtt3Builder().withClientIdentifier("client")

0 commit comments

Comments
 (0)