Skip to content

Commit 6e15917

Browse files
author
Simon MacMullen
committed
Make the AuthMechanism interface rather a lot less stinky.
1 parent d4aed64 commit 6e15917

File tree

4 files changed

+62
-54
lines changed

4 files changed

+62
-54
lines changed

src/com/rabbitmq/client/AuthMechanism.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
package com.rabbitmq.client;
22

3-
import com.rabbitmq.client.impl.AMQChannel;
3+
import com.rabbitmq.client.impl.LongString;
44

55
import java.io.IOException;
66

77
/**
8-
* A pluggable authentication mechanism
8+
* A pluggable authentication mechanism. Should be stateless.
99
*/
1010
public interface AuthMechanism {
1111
/**
12-
* Send and receive start-ok / secure / secure-ok until a connection is
13-
* established or an exception thrown.
14-
*
15-
* @param channel to send methods on
12+
* Handle one round of challenge-response
13+
* @param round zero-based counter of the current round
14+
* @param challenge the challenge this round, or null on round 0.
1615
* @param factory for reference to e.g. username and password.
17-
* @return the Connection.Tune method sent by the server after authentication
18-
* @throws IOException if the authentication failed or something else went wrong
16+
* @return response
17+
* @throws IOException
1918
*/
20-
AMQP.Connection.Tune doLogin(AMQChannel channel, ConnectionFactory factory) throws IOException;
19+
LongString handleChallenge(int round, LongString challenge, ConnectionFactory factory);
2120

2221
/**
2322
* The name of the authentication mechanism, as negotiated on the wire

src/com/rabbitmq/client/impl/AMQConnection.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,30 @@ public void start()
283283
throw new IOException("No compatible authentication mechanism found - " +
284284
"server offered [" + connStart.getMechanisms() + "]");
285285
}
286-
AMQP.Connection.Tune connTune = mechanism.doLogin(_channel0, _factory);
286+
287+
int round = 0;
288+
LongString challenge = null;
289+
AMQP.Connection.Tune connTune = null;
290+
do {
291+
LongString response = mechanism.handleChallenge(round, challenge, _factory);
292+
Method method = (round == 0)
293+
? new AMQImpl.Connection.StartOk(_clientProperties,
294+
mechanism.getName(), response,
295+
"en_US")
296+
: new AMQImpl.Connection.SecureOk(response);
297+
298+
try {
299+
Method serverResponse = _channel0.rpc(method).getMethod();
300+
if (serverResponse instanceof AMQP.Connection.Tune) {
301+
connTune = (AMQP.Connection.Tune) serverResponse;
302+
} else {
303+
challenge = ((AMQP.Connection.Secure) serverResponse).getChallenge();
304+
round++;
305+
}
306+
} catch (ShutdownSignalException e) {
307+
throw AMQChannel.wrap(e, "Possibly caused by authentication failure");
308+
}
309+
} while (connTune == null);
287310

288311
int channelMax =
289312
negotiatedMaxValue(_factory.getRequestedChannelMax(),

src/com/rabbitmq/client/impl/PlainMechanism.java

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,16 @@
11
package com.rabbitmq.client.impl;
22

3-
import com.rabbitmq.client.AMQP;
43
import com.rabbitmq.client.AuthMechanism;
54
import com.rabbitmq.client.ConnectionFactory;
6-
import com.rabbitmq.client.ShutdownSignalException;
7-
8-
import java.io.IOException;
95

106
/**
117
* The PLAIN auth mechanism
128
*/
139
public class PlainMechanism implements AuthMechanism {
14-
public AMQP.Connection.Tune doLogin(AMQChannel channel,
15-
ConnectionFactory factory) throws IOException {
16-
LongString saslResponse = LongStringHelper.asLongString(
17-
"\0" + factory.getUsername() + "\0" + factory.getPassword());
18-
AMQImpl.Connection.StartOk startOk =
19-
new AMQImpl.Connection.StartOk(factory.getClientProperties(), getName(),
20-
saslResponse, "en_US");
21-
22-
try {
23-
return (AMQP.Connection.Tune) channel.rpc(startOk).getMethod();
24-
} catch (ShutdownSignalException e) {
25-
throw AMQChannel.wrap(e, "Possibly caused by authentication failure");
26-
}
10+
public LongString handleChallenge(int round, LongString challenge,
11+
ConnectionFactory factory) {
12+
return LongStringHelper.asLongString("\0" + factory.getUsername() +
13+
"\0" + factory.getPassword());
2714
}
2815

2916
public String getName() {

src/com/rabbitmq/client/impl/ScramMD5Mechanism.java

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package com.rabbitmq.client.impl;
22

3-
import com.rabbitmq.client.AMQP;
43
import com.rabbitmq.client.AuthMechanism;
54
import com.rabbitmq.client.ConnectionFactory;
6-
import com.rabbitmq.client.ShutdownSignalException;
75

8-
import java.io.IOException;
6+
import java.io.UnsupportedEncodingException;
97
import java.security.MessageDigest;
108
import java.security.NoSuchAlgorithmException;
119
import java.util.Arrays;
@@ -25,39 +23,40 @@ This is only somewhat improved security over PLAIN (if you can
2523
*/
2624

2725
public class ScramMD5Mechanism implements AuthMechanism {
28-
public AMQP.Connection.Tune doLogin(AMQChannel channel,
29-
ConnectionFactory factory) throws IOException {
30-
try {
31-
LongString resp1 = LongStringHelper.asLongString(factory.getUsername());
32-
AMQImpl.Connection.StartOk startOk =
33-
new AMQImpl.Connection.StartOk(factory.getClientProperties(), getName(),
34-
resp1, "en_US");
35-
AMQP.Connection.Secure secure =
36-
(AMQP.Connection.Secure) channel.rpc(startOk).getMethod();
37-
byte[] challenge = secure.getChallenge().getBytes();
38-
byte[] salt1 = Arrays.copyOfRange(challenge, 0, 4);
39-
byte[] salt2 = Arrays.copyOfRange(challenge, 4, 8);
40-
41-
MessageDigest digest1 = MessageDigest.getInstance("MD5");
42-
MessageDigest digest2 = MessageDigest.getInstance("MD5");
43-
byte[] d1 = digest1.digest(concat(salt1, factory.getPassword().getBytes("utf-8")));
44-
byte[] d2 = digest2.digest(concat(salt2, d1));
26+
public LongString handleChallenge(int round, LongString challengeStr,
27+
ConnectionFactory factory) {
28+
if (round == 0) {
29+
return LongStringHelper.asLongString(factory.getUsername());
30+
} else {
31+
try {
32+
byte[] challenge = challengeStr.getBytes();
33+
byte[] salt1 = Arrays.copyOfRange(challenge, 0, 4);
34+
byte[] salt2 = Arrays.copyOfRange(challenge, 4, 8);
4535

46-
AMQImpl.Connection.SecureOk secureOk =
47-
new AMQImpl.Connection.SecureOk(LongStringHelper.asLongString(d2));
36+
byte[] pw = factory.getPassword().getBytes("utf-8");
37+
byte[] d = digest(salt2, digest(salt1, pw));
4838

49-
return (AMQP.Connection.Tune) channel.rpc(secureOk).getMethod();
50-
} catch (NoSuchAlgorithmException e) {
51-
throw new RuntimeException(e);
52-
} catch (ShutdownSignalException e) {
53-
throw AMQChannel.wrap(e, "Possibly caused by authentication failure");
39+
return LongStringHelper.asLongString(d);
40+
} catch (UnsupportedEncodingException e) {
41+
throw new RuntimeException(e);
42+
}
5443
}
5544
}
5645

5746
public String getName() {
5847
return "RABBIT-SCRAM-MD5";
5948
}
6049

50+
private static byte[] digest(byte[] arr1, byte[] arr2) {
51+
try {
52+
MessageDigest digest = MessageDigest.getInstance("MD5");
53+
return digest.digest(concat(arr1, arr2));
54+
55+
} catch (NoSuchAlgorithmException e) {
56+
throw new RuntimeException(e);
57+
}
58+
}
59+
6160
private static byte[] concat(byte[] first, byte[] second) {
6261
byte[] result = Arrays.copyOf(first, first.length + second.length);
6362
System.arraycopy(second, 0, result, first.length, second.length);

0 commit comments

Comments
 (0)