X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=src%2Fmain%2Fjava%2Forg%2Fonap%2Fdmaap%2FkafkaAuthorize%2FPlainSaslServer1.java;fp=src%2Fmain%2Fjava%2Forg%2Fonap%2Fdmaap%2FkafkaAuthorize%2FPlainSaslServer1.java;h=6213b9bfe0c7480e89665cf3b8028e4f8f0dbf63;hb=96b4eae15f6e43154d47b1fc2442f1345f405c0b;hp=508d5837d0ec37258eb35dd72d722c1bafcfcf17;hpb=d1118202d8eb04babd7fa1151a5d01d85c6e93dd;p=dmaap%2Fkafka11aaf.git diff --git a/src/main/java/org/onap/dmaap/kafkaAuthorize/PlainSaslServer1.java b/src/main/java/org/onap/dmaap/kafkaAuthorize/PlainSaslServer1.java index 508d583..6213b9b 100644 --- a/src/main/java/org/onap/dmaap/kafkaAuthorize/PlainSaslServer1.java +++ b/src/main/java/org/onap/dmaap/kafkaAuthorize/PlainSaslServer1.java @@ -1,4 +1,4 @@ -/******************************************************************************* +/****************************************************************************** * ============LICENSE_START======================================================= * org.onap.dmaap * ================================================================================ @@ -21,18 +21,25 @@ package org.onap.dmaap.kafkaAuthorize; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; +import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; import javax.security.sasl.Sasl; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import javax.security.sasl.SaslServerFactory; +import org.apache.kafka.common.errors.SaslAuthenticationException; import org.apache.kafka.common.security.JaasContext; import org.apache.kafka.common.security.authenticator.SaslServerCallbackHandler; - +import org.apache.kafka.common.security.plain.PlainAuthenticateCallback; +import org.apache.kafka.common.security.plain.internals.PlainSaslServer; import org.onap.dmaap.commonauth.kafka.base.authorization.AuthorizationProviderFactory; /** @@ -51,126 +58,152 @@ import org.onap.dmaap.commonauth.kafka.base.authorization.AuthorizationProviderF */ public class PlainSaslServer1 implements SaslServer { - public static final String PLAIN_MECHANISM = "PLAIN"; - - private boolean complete; - private String authorizationID; - - - @Override - public byte[] evaluateResponse(byte[] response) throws SaslException { - /* - * Message format (from https://tools.ietf.org/html/rfc4616): - * - * message = [authzid] UTF8NUL authcid UTF8NUL passwd authcid = 1*SAFE ; - * MUST accept up to 255 octets authzid = 1*SAFE ; MUST accept up to 255 - * octets passwd = 1*SAFE ; MUST accept up to 255 octets UTF8NUL = %x00 - * ; UTF-8 encoded NUL character - * - * SAFE = UTF1 / UTF2 / UTF3 / UTF4 ;; any UTF-8 encoded Unicode - * character except NUL - */ - - String[] tokens; - try { - tokens = new String(response, "UTF-8").split("\u0000"); - } catch (UnsupportedEncodingException e) { - throw new SaslException("UTF-8 encoding not supported", e); - } - if (tokens.length != 3) - throw new SaslException("Invalid SASL/PLAIN response: expected 3 tokens, got " + tokens.length); - authorizationID = tokens[0]; - String username = tokens[1]; - String password = tokens[2]; - - if (username.isEmpty()) { - throw new SaslException("Authentication failed: username not specified"); - } - if (password.isEmpty()) { - throw new SaslException("Authentication failed: password not specified"); - } - if (authorizationID.isEmpty()) - authorizationID = username; - - String aafResponse = "Not Verified"; + public static final String PLAIN_MECHANISM = "PLAIN"; + + private boolean complete; + private String authorizationId; + + + /** + * @throws SaslAuthenticationException if username/password combination is invalid or if the requested + * authorization id is not the same as username. + *

+ * Note: This method may throw {@link SaslAuthenticationException} to provide custom error messages + * to clients. But care should be taken to avoid including any information in the exception message that + * should not be leaked to unauthenticated clients. It may be safer to throw {@link SaslException} in + * some cases so that a standard error message is returned to clients. + *

+ */ + @Override + public byte[] evaluateResponse(byte[] responseBytes) throws SaslAuthenticationException { + /* + * Message format (from https://tools.ietf.org/html/rfc4616): + * + * message = [authzid] UTF8NUL authcid UTF8NUL passwd + * authcid = 1*SAFE ; MUST accept up to 255 octets + * authzid = 1*SAFE ; MUST accept up to 255 octets + * passwd = 1*SAFE ; MUST accept up to 255 octets + * UTF8NUL = %x00 ; UTF-8 encoded NUL character + * + * SAFE = UTF1 / UTF2 / UTF3 / UTF4 + * ;; any UTF-8 encoded Unicode character except NUL + */ + String response = new String(responseBytes, StandardCharsets.UTF_8); + List tokens = extractTokens(response); + String authorizationIdFromClient = tokens.get(0); + String username = tokens.get(1); + String password = tokens.get(2); + + if (username.isEmpty()) { + throw new SaslAuthenticationException("Authentication failed: username not specified"); + } + if (password.isEmpty()) { + throw new SaslAuthenticationException("Authentication failed: password not specified"); + } + + String aafResponse = "Not Verified"; try { aafResponse = AuthorizationProviderFactory.getProviderFactory().getProvider().authenticate(username, password); } catch (Exception e) { } - if (null != aafResponse) { - throw new SaslException("Authentication failed: " + aafResponse + " User " + username); + throw new SaslAuthenticationException("Authentication failed: " + aafResponse + " User " + username); } - complete = true; - return new byte[0]; - } - - @Override - public String getAuthorizationID() { - if (!complete) - throw new IllegalStateException("Authentication exchange has not completed"); - return authorizationID; - } - - @Override - public String getMechanismName() { - return PLAIN_MECHANISM; - } - - @Override - public Object getNegotiatedProperty(String propName) { - if (!complete) - throw new IllegalStateException("Authentication exchange has not completed"); - return null; - } - - @Override - public boolean isComplete() { - return complete; - } - - @Override - public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException { - if (!complete) - throw new IllegalStateException("Authentication exchange has not completed"); - return Arrays.copyOfRange(incoming, offset, offset + len); - } - - @Override - public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException { - if (!complete) - throw new IllegalStateException("Authentication exchange has not completed"); - return Arrays.copyOfRange(outgoing, offset, offset + len); - } - - @Override - public void dispose() throws SaslException { - } - - public static class PlainSaslServerFactory1 implements SaslServerFactory { - - @Override - public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map props, - CallbackHandler cbh) throws SaslException { - - if (!PLAIN_MECHANISM.equals(mechanism)) - throw new SaslException( - String.format("Mechanism \'%s\' is not supported. Only PLAIN is supported.", mechanism)); - - return new PlainSaslServer1(); - } - @Override - public String[] getMechanismNames(Map props) { - if (props == null) - return new String[] { PLAIN_MECHANISM }; - String noPlainText = (String) props.get(Sasl.POLICY_NOPLAINTEXT); - if ("true".equals(noPlainText)) - return new String[] {}; - else - return new String[] { PLAIN_MECHANISM }; - } - } + if (!authorizationIdFromClient.isEmpty() && !authorizationIdFromClient.equals(username)) + throw new SaslAuthenticationException("Authentication failed: Client requested an authorization id that is different from username"); + + this.authorizationId = username; + + complete = true; + return new byte[0]; + } + + private List extractTokens(String string) { + List tokens = new ArrayList<>(); + int startIndex = 0; + for (int i = 0; i < 4; ++i) { + int endIndex = string.indexOf("\u0000", startIndex); + if (endIndex == -1) { + tokens.add(string.substring(startIndex)); + break; + } + tokens.add(string.substring(startIndex, endIndex)); + startIndex = endIndex + 1; + } + + if (tokens.size() != 3) + throw new SaslAuthenticationException("Invalid SASL/PLAIN response: expected 3 tokens, got " + + tokens.size()); + + return tokens; + } + + @Override + public String getAuthorizationID() { + if (!complete) + throw new IllegalStateException("Authentication exchange has not completed"); + return authorizationId; + } + + @Override + public String getMechanismName() { + return PLAIN_MECHANISM; + } + + @Override + public Object getNegotiatedProperty(String propName) { + if (!complete) + throw new IllegalStateException("Authentication exchange has not completed"); + return null; + } + + @Override + public boolean isComplete() { + return complete; + } + + @Override + public byte[] unwrap(byte[] incoming, int offset, int len) { + if (!complete) + throw new IllegalStateException("Authentication exchange has not completed"); + return Arrays.copyOfRange(incoming, offset, offset + len); + } + + @Override + public byte[] wrap(byte[] outgoing, int offset, int len) { + if (!complete) + throw new IllegalStateException("Authentication exchange has not completed"); + return Arrays.copyOfRange(outgoing, offset, offset + len); + } + + @Override + public void dispose() { + } + + public static class PlainSaslServerFactory1 implements SaslServerFactory { + + @Override + public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map props, CallbackHandler cbh) + throws SaslException { + + if (!PLAIN_MECHANISM.equals(mechanism)) + throw new SaslException(String.format("Mechanism \'%s\' is not supported. Only PLAIN is supported.", mechanism)); + + return new PlainSaslServer1(); + } + + @Override + public String[] getMechanismNames(Map props) { + if (props == null) return new String[]{PLAIN_MECHANISM}; + String noPlainText = (String) props.get(Sasl.POLICY_NOPLAINTEXT); + if ("true".equals(noPlainText)) + return new String[]{}; + else + return new String[]{PLAIN_MECHANISM}; + } + } } +