/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hive.service.auth; import java.io.IOException; import java.security.Provider; import java.util.ArrayDeque; import java.util.Deque; 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.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.sasl.AuthorizeCallback; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import javax.security.sasl.SaslServerFactory; import org.apache.hive.service.auth.AuthenticationProviderFactory.AuthMethods; /** * Sun JDK only provides a PLAIN client and no server. This class implements the Plain SASL server * conforming to RFC #4616 (http://www.ietf.org/rfc/rfc4616.txt). */ public class PlainSaslServer implements SaslServer { public static final String PLAIN_METHOD = "PLAIN"; private String user; private final CallbackHandler handler; PlainSaslServer(CallbackHandler handler, String authMethodStr) throws SaslException { this.handler = handler; AuthMethods.getValidAuthMethod(authMethodStr); } @Override public String getMechanismName() { return PLAIN_METHOD; } @Override public byte[] evaluateResponse(byte[] response) throws SaslException { try { // parse the response // message = [authzid] UTF8NUL authcid UTF8NUL passwd' Deque<String> tokenList = new ArrayDeque<String>(); StringBuilder messageToken = new StringBuilder(); for (byte b : response) { if (b == 0) { tokenList.addLast(messageToken.toString()); messageToken = new StringBuilder(); } else { messageToken.append((char) b); } } tokenList.addLast(messageToken.toString()); // validate response if (tokenList.size() < 2 || tokenList.size() > 3) { throw new SaslException("Invalid message format"); } String passwd = tokenList.removeLast(); user = tokenList.removeLast(); // optional authzid String authzId; if (tokenList.isEmpty()) { authzId = user; } else { authzId = tokenList.removeLast(); } if (user == null || user.isEmpty()) { throw new SaslException("No user name provided"); } if (passwd == null || passwd.isEmpty()) { throw new SaslException("No password name provided"); } NameCallback nameCallback = new NameCallback("User"); nameCallback.setName(user); PasswordCallback pcCallback = new PasswordCallback("Password", false); pcCallback.setPassword(passwd.toCharArray()); AuthorizeCallback acCallback = new AuthorizeCallback(user, authzId); Callback[] cbList = {nameCallback, pcCallback, acCallback}; handler.handle(cbList); if (!acCallback.isAuthorized()) { throw new SaslException("Authentication failed"); } } catch (IllegalStateException eL) { throw new SaslException("Invalid message format", eL); } catch (IOException eI) { throw new SaslException("Error validating the login", eI); } catch (UnsupportedCallbackException eU) { throw new SaslException("Error validating the login", eU); } return null; } @Override public boolean isComplete() { return user != null; } @Override public String getAuthorizationID() { return user; } @Override public byte[] unwrap(byte[] incoming, int offset, int len) { throw new UnsupportedOperationException(); } @Override public byte[] wrap(byte[] outgoing, int offset, int len) { throw new UnsupportedOperationException(); } @Override public Object getNegotiatedProperty(String propName) { return null; } @Override public void dispose() {} public static class SaslPlainServerFactory implements SaslServerFactory { @Override public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map<String, ?> props, CallbackHandler cbh) { if (PLAIN_METHOD.equals(mechanism)) { try { return new PlainSaslServer(cbh, protocol); } catch (SaslException e) { /* This is to fulfill the contract of the interface which states that an exception shall be thrown when a SaslServer cannot be created due to an error but null should be returned when a Server can't be created due to the parameters supplied. And the only thing PlainSaslServer can fail on is a non-supported authentication mechanism. That's why we return null instead of throwing the Exception */ return null; } } return null; } @Override public String[] getMechanismNames(Map<String, ?> props) { return new String[] {PLAIN_METHOD}; } } public static class SaslPlainProvider extends Provider { public SaslPlainProvider() { super("HiveSaslPlain", 1.0, "Hive Plain SASL provider"); put("SaslServerFactory.PLAIN", SaslPlainServerFactory.class.getName()); } } }