|
@@ -34,16 +34,18 @@ import javax.security.auth.login.LoginException;
|
|
import javax.servlet.ServletException;
|
|
import javax.servlet.ServletException;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletResponse;
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
|
+
|
|
import java.io.File;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
-import java.security.Principal;
|
|
|
|
import java.security.PrivilegedActionException;
|
|
import java.security.PrivilegedActionException;
|
|
import java.security.PrivilegedExceptionAction;
|
|
import java.security.PrivilegedExceptionAction;
|
|
|
|
+import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.HashMap;
|
|
-import java.util.HashSet;
|
|
|
|
|
|
+import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
import java.util.Properties;
|
|
import java.util.Properties;
|
|
import java.util.Set;
|
|
import java.util.Set;
|
|
|
|
+import java.util.regex.Pattern;
|
|
|
|
|
|
import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
|
|
import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
|
|
|
|
|
|
@@ -140,10 +142,10 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
|
|
*/
|
|
*/
|
|
public static final String NAME_RULES = TYPE + ".name.rules";
|
|
public static final String NAME_RULES = TYPE + ".name.rules";
|
|
|
|
|
|
- private String principal;
|
|
|
|
private String keytab;
|
|
private String keytab;
|
|
private GSSManager gssManager;
|
|
private GSSManager gssManager;
|
|
- private LoginContext loginContext;
|
|
|
|
|
|
+ private Subject serverSubject = new Subject();
|
|
|
|
+ private List<LoginContext> loginContexts = new ArrayList<LoginContext>();
|
|
|
|
|
|
/**
|
|
/**
|
|
* Initializes the authentication handler instance.
|
|
* Initializes the authentication handler instance.
|
|
@@ -159,7 +161,7 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
|
|
@Override
|
|
@Override
|
|
public void init(Properties config) throws ServletException {
|
|
public void init(Properties config) throws ServletException {
|
|
try {
|
|
try {
|
|
- principal = config.getProperty(PRINCIPAL, principal);
|
|
|
|
|
|
+ String principal = config.getProperty(PRINCIPAL);
|
|
if (principal == null || principal.trim().length() == 0) {
|
|
if (principal == null || principal.trim().length() == 0) {
|
|
throw new ServletException("Principal not defined in configuration");
|
|
throw new ServletException("Principal not defined in configuration");
|
|
}
|
|
}
|
|
@@ -170,23 +172,40 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
|
|
if (!new File(keytab).exists()) {
|
|
if (!new File(keytab).exists()) {
|
|
throw new ServletException("Keytab does not exist: " + keytab);
|
|
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};
|
|
|
|
+ }
|
|
|
|
|
|
String nameRules = config.getProperty(NAME_RULES, null);
|
|
String nameRules = config.getProperty(NAME_RULES, null);
|
|
if (nameRules != null) {
|
|
if (nameRules != null) {
|
|
KerberosName.setRules(nameRules);
|
|
KerberosName.setRules(nameRules);
|
|
}
|
|
}
|
|
|
|
|
|
- Set<Principal> principals = new HashSet<Principal>();
|
|
|
|
- principals.add(new KerberosPrincipal(principal));
|
|
|
|
- Subject subject = new Subject(false, principals, new HashSet<Object>(), new HashSet<Object>());
|
|
|
|
-
|
|
|
|
- KerberosConfiguration kerberosConfiguration = new KerberosConfiguration(keytab, principal);
|
|
|
|
-
|
|
|
|
- LOG.info("Login using keytab "+keytab+", for principal "+principal);
|
|
|
|
- loginContext = new LoginContext("", subject, null, kerberosConfiguration);
|
|
|
|
- loginContext.login();
|
|
|
|
-
|
|
|
|
- Subject serverSubject = loginContext.getSubject();
|
|
|
|
|
|
+ for (String spnegoPrincipal : spnegoPrincipals) {
|
|
|
|
+ LOG.info("Login using keytab {}, for principal {}",
|
|
|
|
+ keytab, principal);
|
|
|
|
+ final KerberosConfiguration kerberosConfiguration =
|
|
|
|
+ new KerberosConfiguration(keytab, spnegoPrincipal);
|
|
|
|
+ final LoginContext loginContext =
|
|
|
|
+ new LoginContext("", serverSubject, null, kerberosConfiguration);
|
|
|
|
+ try {
|
|
|
|
+ loginContext.login();
|
|
|
|
+ } catch (LoginException le) {
|
|
|
|
+ LOG.warn("Failed to login as [{}]", spnegoPrincipal, le);
|
|
|
|
+ throw new AuthenticationException(le);
|
|
|
|
+ }
|
|
|
|
+ loginContexts.add(loginContext);
|
|
|
|
+ }
|
|
try {
|
|
try {
|
|
gssManager = Subject.doAs(serverSubject, new PrivilegedExceptionAction<GSSManager>() {
|
|
gssManager = Subject.doAs(serverSubject, new PrivilegedExceptionAction<GSSManager>() {
|
|
|
|
|
|
@@ -198,7 +217,6 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
|
|
} catch (PrivilegedActionException ex) {
|
|
} catch (PrivilegedActionException ex) {
|
|
throw ex.getException();
|
|
throw ex.getException();
|
|
}
|
|
}
|
|
- LOG.info("Initialized, principal [{}] from keytab [{}]", principal, keytab);
|
|
|
|
} catch (Exception ex) {
|
|
} catch (Exception ex) {
|
|
throw new ServletException(ex);
|
|
throw new ServletException(ex);
|
|
}
|
|
}
|
|
@@ -211,14 +229,16 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
|
|
*/
|
|
*/
|
|
@Override
|
|
@Override
|
|
public void destroy() {
|
|
public void destroy() {
|
|
- try {
|
|
|
|
- if (loginContext != null) {
|
|
|
|
|
|
+ keytab = null;
|
|
|
|
+ serverSubject = null;
|
|
|
|
+ for (LoginContext loginContext : loginContexts) {
|
|
|
|
+ try {
|
|
loginContext.logout();
|
|
loginContext.logout();
|
|
- loginContext = null;
|
|
|
|
|
|
+ } catch (LoginException ex) {
|
|
|
|
+ LOG.warn(ex.getMessage(), ex);
|
|
}
|
|
}
|
|
- } catch (LoginException ex) {
|
|
|
|
- LOG.warn(ex.getMessage(), ex);
|
|
|
|
}
|
|
}
|
|
|
|
+ loginContexts.clear();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -233,12 +253,12 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Returns the Kerberos principal used by the authentication handler.
|
|
|
|
|
|
+ * Returns the Kerberos principals used by the authentication handler.
|
|
*
|
|
*
|
|
- * @return the Kerberos principal used by the authentication handler.
|
|
|
|
|
|
+ * @return the Kerberos principals used by the authentication handler.
|
|
*/
|
|
*/
|
|
- protected String getPrincipal() {
|
|
|
|
- return principal;
|
|
|
|
|
|
+ protected Set<KerberosPrincipal> getPrincipals() {
|
|
|
|
+ return serverSubject.getPrincipals(KerberosPrincipal.class);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -304,7 +324,7 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
|
|
authorization = authorization.substring(KerberosAuthenticator.NEGOTIATE.length()).trim();
|
|
authorization = authorization.substring(KerberosAuthenticator.NEGOTIATE.length()).trim();
|
|
final Base64 base64 = new Base64(0);
|
|
final Base64 base64 = new Base64(0);
|
|
final byte[] clientToken = base64.decode(authorization);
|
|
final byte[] clientToken = base64.decode(authorization);
|
|
- Subject serverSubject = loginContext.getSubject();
|
|
|
|
|
|
+ final String serverName = request.getServerName();
|
|
try {
|
|
try {
|
|
token = Subject.doAs(serverSubject, new PrivilegedExceptionAction<AuthenticationToken>() {
|
|
token = Subject.doAs(serverSubject, new PrivilegedExceptionAction<AuthenticationToken>() {
|
|
|
|
|
|
@@ -314,15 +334,15 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
|
|
GSSContext gssContext = null;
|
|
GSSContext gssContext = null;
|
|
GSSCredential gssCreds = null;
|
|
GSSCredential gssCreds = null;
|
|
try {
|
|
try {
|
|
- if (IBM_JAVA) {
|
|
|
|
- // IBM JDK needs non-null credentials to be passed to createContext here, with
|
|
|
|
- // SPNEGO mechanism specified, otherwise JGSS will use its default mechanism
|
|
|
|
- // only, which is Kerberos V5.
|
|
|
|
- gssCreds = gssManager.createCredential(null, GSSCredential.INDEFINITE_LIFETIME,
|
|
|
|
- new Oid[]{KerberosUtil.getOidInstance("GSS_SPNEGO_MECH_OID"),
|
|
|
|
- KerberosUtil.getOidInstance("GSS_KRB5_MECH_OID")},
|
|
|
|
- GSSCredential.ACCEPT_ONLY);
|
|
|
|
- }
|
|
|
|
|
|
+ gssCreds = gssManager.createCredential(
|
|
|
|
+ gssManager.createName(
|
|
|
|
+ KerberosUtil.getServicePrincipal("HTTP", serverName),
|
|
|
|
+ KerberosUtil.getOidInstance("NT_GSS_KRB5_PRINCIPAL")),
|
|
|
|
+ GSSCredential.INDEFINITE_LIFETIME,
|
|
|
|
+ new Oid[]{
|
|
|
|
+ KerberosUtil.getOidInstance("GSS_SPNEGO_MECH_OID"),
|
|
|
|
+ KerberosUtil.getOidInstance("GSS_KRB5_MECH_OID")},
|
|
|
|
+ GSSCredential.ACCEPT_ONLY);
|
|
gssContext = gssManager.createContext(gssCreds);
|
|
gssContext = gssManager.createContext(gssCreds);
|
|
byte[] serverToken = gssContext.acceptSecContext(clientToken, 0, clientToken.length);
|
|
byte[] serverToken = gssContext.acceptSecContext(clientToken, 0, clientToken.length);
|
|
if (serverToken != null && serverToken.length > 0) {
|
|
if (serverToken != null && serverToken.length > 0) {
|