com.amazon.redshift.jre7.sasl.ScramAuthenticator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redshift-jdbc42 Show documentation
Show all versions of redshift-jdbc42 Show documentation
Java JDBC 4.2 (JRE 8+) driver for Redshift database
/*
* Copyright (c) 2017, PostgreSQL Global Development Group
* See the LICENSE file in the project root for more information.
*/
package com.amazon.redshift.jre7.sasl;
import com.amazon.redshift.core.RedshiftStream;
import com.amazon.redshift.util.GT;
import com.amazon.redshift.util.RedshiftException;
import com.amazon.redshift.util.RedshiftState;
import com.ongres.scram.client.ScramClient;
import com.ongres.scram.client.ScramSession;
import com.ongres.scram.common.exception.ScramException;
import com.ongres.scram.common.exception.ScramInvalidServerSignatureException;
import com.ongres.scram.common.exception.ScramParseException;
import com.ongres.scram.common.exception.ScramServerErrorException;
import com.ongres.scram.common.stringprep.StringPreparations;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class ScramAuthenticator {
private final String user;
private final String password;
private final RedshiftStream pgStream;
private ScramClient scramClient;
private ScramSession scramSession;
private ScramSession.ServerFirstProcessor serverFirstProcessor;
private ScramSession.ClientFinalProcessor clientFinalProcessor;
private interface BodySender {
void sendBody(RedshiftStream pgStream) throws IOException;
}
private void sendAuthenticationMessage(int bodyLength, BodySender bodySender)
throws IOException {
pgStream.sendChar('p');
pgStream.sendInteger4(Integer.SIZE / Byte.SIZE + bodyLength);
bodySender.sendBody(pgStream);
pgStream.flush();
}
public ScramAuthenticator(String user, String password, RedshiftStream pgStream) {
this.user = user;
this.password = password;
this.pgStream = pgStream;
}
public void processServerMechanismsAndInit() throws IOException, RedshiftException {
List mechanisms = new ArrayList<>();
do {
mechanisms.add(pgStream.receiveString());
} while (pgStream.peekChar() != 0);
int c = pgStream.receiveChar();
assert c == 0;
if (mechanisms.size() < 1) {
throw new RedshiftException(
GT.tr("No SCRAM mechanism(s) advertised by the server"),
RedshiftState.CONNECTION_REJECTED
);
}
try {
scramClient = ScramClient
.channelBinding(ScramClient.ChannelBinding.NO)
.stringPreparation(StringPreparations.NO_PREPARATION)
.selectMechanismBasedOnServerAdvertised(mechanisms.toArray(new String[]{}))
.setup();
} catch (IllegalArgumentException e) {
throw new RedshiftException(
GT.tr("Invalid or unsupported by client SCRAM mechanisms", e),
RedshiftState.CONNECTION_REJECTED
);
}
/* if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, " Using SCRAM mechanism {0}", scramClient.getScramMechanism().getName());
} */
scramSession =
scramClient.scramSession("*"); // Real username is ignored by server, uses startup one
}
public void sendScramClientFirstMessage() throws IOException {
String clientFirstMessage = scramSession.clientFirstMessage();
// logger.log(LogLevel.DEBUG, " FE=> SASLInitialResponse( {0} )", clientFirstMessage);
String scramMechanismName = scramClient.getScramMechanism().getName();
final byte[] scramMechanismNameBytes = scramMechanismName.getBytes(StandardCharsets.UTF_8);
final byte[] clientFirstMessageBytes = clientFirstMessage.getBytes(StandardCharsets.UTF_8);
sendAuthenticationMessage(
(scramMechanismNameBytes.length + 1) + 4 + clientFirstMessageBytes.length,
new BodySender() {
@Override
public void sendBody(RedshiftStream pgStream) throws IOException {
pgStream.send(scramMechanismNameBytes);
pgStream.sendChar(0); // List terminated in '\0'
pgStream.sendInteger4(clientFirstMessageBytes.length);
pgStream.send(clientFirstMessageBytes);
}
}
);
}
public void processServerFirstMessage(int length) throws IOException, RedshiftException {
String serverFirstMessage = pgStream.receiveString(length);
// logger.log(Level.FINEST, " <=BE AuthenticationSASLContinue( {0} )", serverFirstMessage);
try {
serverFirstProcessor = scramSession.receiveServerFirstMessage(serverFirstMessage);
} catch (ScramException e) {
throw new RedshiftException(
GT.tr("Invalid server-first-message: {0}", serverFirstMessage),
RedshiftState.CONNECTION_REJECTED,
e
);
}
/* if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST,
" <=BE AuthenticationSASLContinue(salt={0}, iterations={1})",
new Object[] { serverFirstProcessor.getSalt(), serverFirstProcessor.getIteration() }
);
} */
clientFinalProcessor = serverFirstProcessor.clientFinalProcessor(password);
String clientFinalMessage = clientFinalProcessor.clientFinalMessage();
// logger.log(Level.FINEST, " FE=> SASLResponse( {0} )", clientFinalMessage);
final byte[] clientFinalMessageBytes = clientFinalMessage.getBytes(StandardCharsets.UTF_8);
sendAuthenticationMessage(
clientFinalMessageBytes.length,
new BodySender() {
@Override
public void sendBody(RedshiftStream pgStream) throws IOException {
pgStream.send(clientFinalMessageBytes);
}
}
);
}
public void verifyServerSignature(int length) throws IOException, RedshiftException {
String serverFinalMessage = pgStream.receiveString(length);
// logger.log(Level.FINEST, " <=BE AuthenticationSASLFinal( {0} )", serverFinalMessage);
try {
clientFinalProcessor.receiveServerFinalMessage(serverFinalMessage);
} catch (ScramParseException e) {
throw new RedshiftException(
GT.tr("Invalid server-final-message: {0}", serverFinalMessage),
RedshiftState.CONNECTION_REJECTED,
e
);
} catch (ScramServerErrorException e) {
throw new RedshiftException(
GT.tr("SCRAM authentication failed, server returned error: {0}",
e.getError().getErrorMessage()),
RedshiftState.CONNECTION_REJECTED,
e
);
} catch (ScramInvalidServerSignatureException e) {
throw new RedshiftException(
GT.tr("Invalid server SCRAM signature"),
RedshiftState.CONNECTION_REJECTED,
e
);
}
}
}