[Kerberos] Add support for Kerberos V5 Oid (#35764)

Clients can use the Kerberos V5 security mechanism and when it
used this to establish security context it failed to do so as
Elasticsearch server only accepted Spengo mechanism.
This commit adds support to accept Kerberos V5 credentials
over spnego.

Closes #34763
This commit is contained in:
Yogesh Gaikwad 2018-11-28 13:29:43 +11:00 committed by GitHub
parent 19ed17195f
commit e50e0f997a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 24 additions and 17 deletions

View File

@ -41,12 +41,14 @@ import javax.security.auth.login.LoginException;
* It may respond with token which needs to be communicated with the peer. * It may respond with token which needs to be communicated with the peer.
*/ */
public class KerberosTicketValidator { public class KerberosTicketValidator {
static final Oid SPNEGO_OID = getSpnegoOid(); static final Oid SPNEGO_OID = getOid("1.3.6.1.5.5.2");
static final Oid KERBEROS_V5_OID = getOid("1.2.840.113554.1.2.2");
static final Oid[] SUPPORTED_OIDS = new Oid[] { SPNEGO_OID, KERBEROS_V5_OID };
private static Oid getSpnegoOid() { private static Oid getOid(final String id) {
Oid oid = null; Oid oid = null;
try { try {
oid = new Oid("1.3.6.1.5.5.2"); oid = new Oid(id);
} catch (GSSException gsse) { } catch (GSSException gsse) {
throw ExceptionsHelper.convertToRuntime(gsse); throw ExceptionsHelper.convertToRuntime(gsse);
} }
@ -152,7 +154,7 @@ public class KerberosTicketValidator {
*/ */
private static GSSCredential createCredentials(final GSSManager gssManager, final Subject subject) throws PrivilegedActionException { private static GSSCredential createCredentials(final GSSManager gssManager, final Subject subject) throws PrivilegedActionException {
return doAsWrapper(subject, (PrivilegedExceptionAction<GSSCredential>) () -> gssManager.createCredential(null, return doAsWrapper(subject, (PrivilegedExceptionAction<GSSCredential>) () -> gssManager.createCredential(null,
GSSCredential.DEFAULT_LIFETIME, SPNEGO_OID, GSSCredential.ACCEPT_ONLY)); GSSCredential.DEFAULT_LIFETIME, SUPPORTED_OIDS, GSSCredential.ACCEPT_ONLY));
} }
/** /**

View File

@ -37,8 +37,8 @@ public class KerberosTicketValidatorTests extends KerberosTestCase {
// Client login and init token preparation // Client login and init token preparation
final String clientUserName = randomFrom(clientUserNames); final String clientUserName = randomFrom(clientUserNames);
try (SpnegoClient spnegoClient = try (SpnegoClient spnegoClient = new SpnegoClient(principalName(clientUserName), new SecureString("pwd".toCharArray()),
new SpnegoClient(principalName(clientUserName), new SecureString("pwd".toCharArray()), principalName("differentServer"))) { principalName("differentServer"), randomFrom(KerberosTicketValidator.SUPPORTED_OIDS))) {
final String base64KerbToken = spnegoClient.getBase64EncodedTokenForSpnegoHeader(); final String base64KerbToken = spnegoClient.getBase64EncodedTokenForSpnegoHeader();
assertThat(base64KerbToken, is(notNullValue())); assertThat(base64KerbToken, is(notNullValue()));
@ -80,7 +80,7 @@ public class KerberosTicketValidatorTests extends KerberosTestCase {
// Client login and init token preparation // Client login and init token preparation
final String clientUserName = randomFrom(clientUserNames); final String clientUserName = randomFrom(clientUserNames);
try (SpnegoClient spnegoClient = new SpnegoClient(principalName(clientUserName), new SecureString("pwd".toCharArray()), try (SpnegoClient spnegoClient = new SpnegoClient(principalName(clientUserName), new SecureString("pwd".toCharArray()),
principalName(randomFrom(serviceUserNames)));) { principalName(randomFrom(serviceUserNames)), randomFrom(KerberosTicketValidator.SUPPORTED_OIDS));) {
final String base64KerbToken = spnegoClient.getBase64EncodedTokenForSpnegoHeader(); final String base64KerbToken = spnegoClient.getBase64EncodedTokenForSpnegoHeader();
assertThat(base64KerbToken, is(notNullValue())); assertThat(base64KerbToken, is(notNullValue()));
@ -100,7 +100,8 @@ public class KerberosTicketValidatorTests extends KerberosTestCase {
final String clientUserName = randomFrom(clientUserNames); final String clientUserName = randomFrom(clientUserNames);
final SecureString password = new SecureString("pwd".toCharArray()); final SecureString password = new SecureString("pwd".toCharArray());
final String servicePrincipalName = principalName(randomFrom(serviceUserNames)); final String servicePrincipalName = principalName(randomFrom(serviceUserNames));
try (SpnegoClient spnegoClient = new SpnegoClient(principalName(clientUserName), password, servicePrincipalName)) { try (SpnegoClient spnegoClient = new SpnegoClient(principalName(clientUserName), password, servicePrincipalName,
randomFrom(KerberosTicketValidator.SUPPORTED_OIDS))) {
final String base64KerbToken = spnegoClient.getBase64EncodedTokenForSpnegoHeader(); final String base64KerbToken = spnegoClient.getBase64EncodedTokenForSpnegoHeader();
assertThat(base64KerbToken, is(notNullValue())); assertThat(base64KerbToken, is(notNullValue()));

View File

@ -50,8 +50,8 @@ public class SimpleKdcLdapServerTests extends KerberosTestCase {
final String serviceUserName = randomFrom(serviceUserNames); final String serviceUserName = randomFrom(serviceUserNames);
// Client login and init token preparation // Client login and init token preparation
final String clientUserName = randomFrom(clientUserNames); final String clientUserName = randomFrom(clientUserNames);
try (SpnegoClient spnegoClient = try (SpnegoClient spnegoClient = new SpnegoClient(principalName(clientUserName), new SecureString("pwd".toCharArray()),
new SpnegoClient(principalName(clientUserName), new SecureString("pwd".toCharArray()), principalName(serviceUserName));) { principalName(serviceUserName), randomFrom(KerberosTicketValidator.SUPPORTED_OIDS));) {
final String base64KerbToken = spnegoClient.getBase64EncodedTokenForSpnegoHeader(); final String base64KerbToken = spnegoClient.getBase64EncodedTokenForSpnegoHeader();
assertThat(base64KerbToken, is(notNullValue())); assertThat(base64KerbToken, is(notNullValue()));
final KerberosAuthenticationToken kerbAuthnToken = new KerberosAuthenticationToken(Base64.getDecoder().decode(base64KerbToken)); final KerberosAuthenticationToken kerbAuthnToken = new KerberosAuthenticationToken(Base64.getDecoder().decode(base64KerbToken));

View File

@ -6,17 +6,17 @@
package org.elasticsearch.xpack.security.authc.kerberos; package org.elasticsearch.xpack.security.authc.kerberos;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.xpack.security.authc.kerberos.KerberosTicketValidator;
import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager; import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName; import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import java.io.IOException; import java.io.IOException;
import java.security.AccessController; import java.security.AccessController;
@ -43,6 +43,7 @@ import javax.security.auth.login.LoginException;
/** /**
* This class is used as a Spnego client during testing and handles SPNEGO * This class is used as a Spnego client during testing and handles SPNEGO
* interactions using GSS context negotiation.<br> * interactions using GSS context negotiation.<br>
* It supports Kerberos V5 and Spnego mechanism.<br>
* It is not advisable to share a SpnegoClient between threads as there is no * It is not advisable to share a SpnegoClient between threads as there is no
* synchronization in place, internally this depends on {@link GSSContext} for * synchronization in place, internally this depends on {@link GSSContext} for
* context negotiation which maintains sequencing for replay detections.<br> * context negotiation which maintains sequencing for replay detections.<br>
@ -62,14 +63,17 @@ class SpnegoClient implements AutoCloseable {
* Creates SpengoClient to interact with given service principal<br> * Creates SpengoClient to interact with given service principal<br>
* Use {@link #close()} to logout {@link LoginContext} and dispose * Use {@link #close()} to logout {@link LoginContext} and dispose
* {@link GSSContext} after usage. * {@link GSSContext} after usage.
*
* @param userPrincipalName User principal name for login as client * @param userPrincipalName User principal name for login as client
* @param password password for client * @param password password for client
* @param servicePrincipalName Service principal name with whom this client * @param servicePrincipalName Service principal name with whom this client
* interacts with. * interacts with.
* @param mechanism the Oid of the desired mechanism. Use (Oid) null to request
* the default mechanism.
* @throws PrivilegedActionException when privileged action threw exception * @throws PrivilegedActionException when privileged action threw exception
* @throws GSSException thrown when GSS API error occurs * @throws GSSException thrown when GSS API error occurs
*/ */
SpnegoClient(final String userPrincipalName, final SecureString password, final String servicePrincipalName) SpnegoClient(final String userPrincipalName, final SecureString password, final String servicePrincipalName, final Oid mechanism)
throws PrivilegedActionException, GSSException { throws PrivilegedActionException, GSSException {
String oldUseSubjectCredsOnlyFlag = null; String oldUseSubjectCredsOnlyFlag = null;
try { try {
@ -81,9 +85,9 @@ class SpnegoClient implements AutoCloseable {
.doPrivileged((PrivilegedExceptionAction<LoginContext>) () -> loginUsingPassword(userPrincipalName, password)); .doPrivileged((PrivilegedExceptionAction<LoginContext>) () -> loginUsingPassword(userPrincipalName, password));
final GSSCredential userCreds = KerberosTestCase.doAsWrapper(loginContext.getSubject(), final GSSCredential userCreds = KerberosTestCase.doAsWrapper(loginContext.getSubject(),
(PrivilegedExceptionAction<GSSCredential>) () -> gssManager.createCredential(gssUserPrincipalName, (PrivilegedExceptionAction<GSSCredential>) () -> gssManager.createCredential(gssUserPrincipalName,
GSSCredential.DEFAULT_LIFETIME, KerberosTicketValidator.SPNEGO_OID, GSSCredential.INITIATE_ONLY)); GSSCredential.DEFAULT_LIFETIME, mechanism, GSSCredential.INITIATE_ONLY));
gssContext = gssManager.createContext(gssServicePrincipalName.canonicalize(KerberosTicketValidator.SPNEGO_OID), gssContext = gssManager.createContext(gssServicePrincipalName.canonicalize(mechanism),
KerberosTicketValidator.SPNEGO_OID, userCreds, GSSCredential.DEFAULT_LIFETIME); mechanism, userCreds, GSSCredential.DEFAULT_LIFETIME);
gssContext.requestMutualAuth(true); gssContext.requestMutualAuth(true);
} catch (PrivilegedActionException pve) { } catch (PrivilegedActionException pve) {
LOGGER.error("privileged action exception, with root cause", pve.getException()); LOGGER.error("privileged action exception, with root cause", pve.getException());