All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler Maven / Gradle / Ivy

There is a newer version: 3.2.0-9
Show newest version
/**
 * Licensed 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. See accompanying LICENSE file.
 */
package org.apache.hadoop.security.authentication.server;

import org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
import io.prestosql.hadoop.$internal.org.apache.commons.codec.binary.Base64;
import org.apache.hadoop.security.authentication.util.KerberosName;
import org.apache.hadoop.security.authentication.util.KerberosUtil;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.Oid;
import io.prestosql.hadoop.$internal.org.slf4j.Logger;
import io.prestosql.hadoop.$internal.org.slf4j.LoggerFactory;

import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KeyTab;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.File;
import java.io.IOException;
import java.security.Principal;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * The {@link KerberosAuthenticationHandler} implements the Kerberos SPNEGO
 * authentication mechanism for HTTP.
 * 

* The supported configuration properties are: *

    *
  • kerberos.principal: the Kerberos principal to used by the server. As * stated by the Kerberos SPNEGO specification, it should be * HTTP/${HOSTNAME}@{REALM}. The realm can be omitted from the * principal as the JDK GSS libraries will use the realm name of the configured * default realm. * It does not have a default value.
  • *
  • kerberos.keytab: the keytab file containing the credentials for the * Kerberos principal. * It does not have a default value.
  • *
  • kerberos.name.rules: kerberos names rules to resolve principal names, see * {@link KerberosName#setRules(String)}
  • *
*/ public class KerberosAuthenticationHandler implements AuthenticationHandler { public static final Logger LOG = LoggerFactory.getLogger( KerberosAuthenticationHandler.class); /** * Constant that identifies the authentication mechanism. */ public static final String TYPE = "kerberos"; /** * Constant for the configuration property that indicates the kerberos * principal. */ public static final String PRINCIPAL = TYPE + ".principal"; /** * Constant for the configuration property that indicates the keytab * file path. */ public static final String KEYTAB = TYPE + ".keytab"; /** * Constant for the configuration property that indicates the Kerberos name * rules for the Kerberos principals. */ public static final String NAME_RULES = TYPE + ".name.rules"; /** * Constant for the configuration property that indicates how auth_to_local * rules are evaluated. */ public static final String RULE_MECHANISM = TYPE + ".name.rules.mechanism"; private String type; private String keytab; private GSSManager gssManager; private Subject serverSubject = new Subject(); /** * Creates a Kerberos SPNEGO authentication handler with the default * auth-token type, kerberos. */ public KerberosAuthenticationHandler() { this(TYPE); } /** * Creates a Kerberos SPNEGO authentication handler with a custom auth-token * type. * * @param type auth-token type. */ public KerberosAuthenticationHandler(String type) { this.type = type; } /** * Initializes the authentication handler instance. *

* It creates a Kerberos context using the principal and keytab specified in * the configuration. *

* This method is invoked by the {@link AuthenticationFilter#init} method. * * @param config configuration properties to initialize the handler. * * @throws ServletException thrown if the handler could not be initialized. */ @Override public void init(Properties config) throws ServletException { try { String principal = config.getProperty(PRINCIPAL); if (principal == null || principal.trim().length() == 0) { throw new ServletException("Principal not defined in configuration"); } keytab = config.getProperty(KEYTAB, keytab); if (keytab == null || keytab.trim().length() == 0) { throw new ServletException("Keytab not defined in configuration"); } File keytabFile = new File(keytab); if (!keytabFile.exists()) { throw new ServletException("Keytab does not exist: " + keytab); } // use all SPNEGO principals in the keytab if a principal isn't // specifically configured final String[] spnegoPrincipals; if (principal.equals("*")) { spnegoPrincipals = KerberosUtil.getPrincipalNames( keytab, Pattern.compile("HTTP/.*")); if (spnegoPrincipals.length == 0) { throw new ServletException("Principals do not exist in the keytab"); } } else { spnegoPrincipals = new String[]{principal}; } KeyTab keytabInstance = KeyTab.getInstance(keytabFile); serverSubject.getPrivateCredentials().add(keytabInstance); for (String spnegoPrincipal : spnegoPrincipals) { Principal krbPrincipal = new KerberosPrincipal(spnegoPrincipal); LOG.info("Using keytab {}, for principal {}", keytab, krbPrincipal); serverSubject.getPrincipals().add(krbPrincipal); } String nameRules = config.getProperty(NAME_RULES, null); if (nameRules != null) { KerberosName.setRules(nameRules); } String ruleMechanism = config.getProperty(RULE_MECHANISM, null); if (ruleMechanism != null) { KerberosName.setRuleMechanism(ruleMechanism); } try { gssManager = Subject.doAs(serverSubject, new PrivilegedExceptionAction() { @Override public GSSManager run() throws Exception { return GSSManager.getInstance(); } }); } catch (PrivilegedActionException ex) { throw ex.getException(); } } catch (Exception ex) { throw new ServletException(ex); } } /** * Releases any resources initialized by the authentication handler. *

* It destroys the Kerberos context. */ @Override public void destroy() { keytab = null; serverSubject = null; } /** * Returns the authentication type of the authentication handler, 'kerberos'. *

* * @return the authentication type of the authentication handler, 'kerberos'. */ @Override public String getType() { return type; } /** * Returns the Kerberos principals used by the authentication handler. * * @return the Kerberos principals used by the authentication handler. */ protected Set getPrincipals() { return serverSubject.getPrincipals(KerberosPrincipal.class); } /** * Returns the keytab used by the authentication handler. * * @return the keytab used by the authentication handler. */ protected String getKeytab() { return keytab; } /** * This is an empty implementation, it always returns TRUE. * * * * @param token the authentication token if any, otherwise NULL. * @param request the HTTP client request. * @param response the HTTP client response. * * @return TRUE * @throws IOException it is never thrown. * @throws AuthenticationException it is never thrown. */ @Override public boolean managementOperation(AuthenticationToken token, HttpServletRequest request, HttpServletResponse response) throws IOException, AuthenticationException { return true; } /** * It enforces the the Kerberos SPNEGO authentication sequence returning an * {@link AuthenticationToken} only after the Kerberos SPNEGO sequence has * completed successfully. * * @param request the HTTP client request. * @param response the HTTP client response. * * @return an authentication token if the Kerberos SPNEGO sequence is complete * and valid, null if it is in progress (in this case the handler * handles the response to the client). * * @throws IOException thrown if an IO error occurred. * @throws AuthenticationException thrown if Kerberos SPNEGO sequence failed. */ @Override public AuthenticationToken authenticate(HttpServletRequest request, final HttpServletResponse response) throws IOException, AuthenticationException { AuthenticationToken token = null; String authorization = request.getHeader( KerberosAuthenticator.AUTHORIZATION); if (authorization == null || !authorization.startsWith(KerberosAuthenticator.NEGOTIATE)) { response.setHeader(WWW_AUTHENTICATE, KerberosAuthenticator.NEGOTIATE); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); if (authorization == null) { LOG.trace("SPNEGO starting for url: {}", request.getRequestURL()); } else { LOG.warn("'" + KerberosAuthenticator.AUTHORIZATION + "' does not start with '" + KerberosAuthenticator.NEGOTIATE + "' : {}", authorization); } } else { authorization = authorization.substring( KerberosAuthenticator.NEGOTIATE.length()).trim(); final Base64 base64 = new Base64(0); final byte[] clientToken = base64.decode(authorization); try { final String serverPrincipal = KerberosUtil.getTokenServerName(clientToken); if (!serverPrincipal.startsWith("HTTP/")) { throw new IllegalArgumentException( "Invalid server principal " + serverPrincipal + "decoded from client request"); } token = Subject.doAs(serverSubject, new PrivilegedExceptionAction() { @Override public AuthenticationToken run() throws Exception { return runWithPrincipal(serverPrincipal, clientToken, base64, response); } }); } catch (PrivilegedActionException ex) { if (ex.getException() instanceof IOException) { throw (IOException) ex.getException(); } else { throw new AuthenticationException(ex.getException()); } } catch (Exception ex) { throw new AuthenticationException(ex); } } return token; } private AuthenticationToken runWithPrincipal(String serverPrincipal, byte[] clientToken, Base64 base64, HttpServletResponse response) throws IOException, GSSException { GSSContext gssContext = null; GSSCredential gssCreds = null; AuthenticationToken token = null; try { LOG.trace("SPNEGO initiated with server principal [{}]", serverPrincipal); gssCreds = this.gssManager.createCredential( this.gssManager.createName(serverPrincipal, KerberosUtil.NT_GSS_KRB5_PRINCIPAL_OID), GSSCredential.INDEFINITE_LIFETIME, new Oid[]{ KerberosUtil.GSS_SPNEGO_MECH_OID, KerberosUtil.GSS_KRB5_MECH_OID }, GSSCredential.ACCEPT_ONLY); gssContext = this.gssManager.createContext(gssCreds); byte[] serverToken = gssContext.acceptSecContext(clientToken, 0, clientToken.length); if (serverToken != null && serverToken.length > 0) { String authenticate = base64.encodeToString(serverToken); response.setHeader(KerberosAuthenticator.WWW_AUTHENTICATE, KerberosAuthenticator.NEGOTIATE + " " + authenticate); } if (!gssContext.isEstablished()) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); LOG.trace("SPNEGO in progress"); } else { String clientPrincipal = gssContext.getSrcName().toString(); KerberosName kerberosName = new KerberosName(clientPrincipal); String userName = kerberosName.getShortName(); token = new AuthenticationToken(userName, clientPrincipal, getType()); response.setStatus(HttpServletResponse.SC_OK); LOG.trace("SPNEGO completed for client principal [{}]", clientPrincipal); } } finally { if (gssContext != null) { gssContext.dispose(); } if (gssCreds != null) { gssCreds.dispose(); } } return token; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy