[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.
*/
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;
try {
oid = new Oid("1.3.6.1.5.5.2");
oid = new Oid(id);
} catch (GSSException gsse) {
throw ExceptionsHelper.convertToRuntime(gsse);
}
@ -152,7 +154,7 @@ public class KerberosTicketValidator {
*/
private static GSSCredential createCredentials(final GSSManager gssManager, final Subject subject) throws PrivilegedActionException {
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
final String clientUserName = randomFrom(clientUserNames);
try (SpnegoClient spnegoClient =
new SpnegoClient(principalName(clientUserName), new SecureString("pwd".toCharArray()), principalName("differentServer"))) {
try (SpnegoClient spnegoClient = new SpnegoClient(principalName(clientUserName), new SecureString("pwd".toCharArray()),
principalName("differentServer"), randomFrom(KerberosTicketValidator.SUPPORTED_OIDS))) {
final String base64KerbToken = spnegoClient.getBase64EncodedTokenForSpnegoHeader();
assertThat(base64KerbToken, is(notNullValue()));
@ -80,7 +80,7 @@ public class KerberosTicketValidatorTests extends KerberosTestCase {
// Client login and init token preparation
final String clientUserName = randomFrom(clientUserNames);
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();
assertThat(base64KerbToken, is(notNullValue()));
@ -100,7 +100,8 @@ public class KerberosTicketValidatorTests extends KerberosTestCase {
final String clientUserName = randomFrom(clientUserNames);
final SecureString password = new SecureString("pwd".toCharArray());
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();
assertThat(base64KerbToken, is(notNullValue()));

View File

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

View File

@ -6,17 +6,17 @@
package org.elasticsearch.xpack.security.authc.kerberos;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.xpack.security.authc.kerberos.KerberosTicketValidator;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import java.io.IOException;
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
* 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
* synchronization in place, internally this depends on {@link GSSContext} for
* 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>
* Use {@link #close()} to logout {@link LoginContext} and dispose
* {@link GSSContext} after usage.
*
* @param userPrincipalName User principal name for login as client
* @param password password for 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 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 {
String oldUseSubjectCredsOnlyFlag = null;
try {
@ -81,9 +85,9 @@ class SpnegoClient implements AutoCloseable {
.doPrivileged((PrivilegedExceptionAction<LoginContext>) () -> loginUsingPassword(userPrincipalName, password));
final GSSCredential userCreds = KerberosTestCase.doAsWrapper(loginContext.getSubject(),
(PrivilegedExceptionAction<GSSCredential>) () -> gssManager.createCredential(gssUserPrincipalName,
GSSCredential.DEFAULT_LIFETIME, KerberosTicketValidator.SPNEGO_OID, GSSCredential.INITIATE_ONLY));
gssContext = gssManager.createContext(gssServicePrincipalName.canonicalize(KerberosTicketValidator.SPNEGO_OID),
KerberosTicketValidator.SPNEGO_OID, userCreds, GSSCredential.DEFAULT_LIFETIME);
GSSCredential.DEFAULT_LIFETIME, mechanism, GSSCredential.INITIATE_ONLY));
gssContext = gssManager.createContext(gssServicePrincipalName.canonicalize(mechanism),
mechanism, userCreds, GSSCredential.DEFAULT_LIFETIME);
gssContext.requestMutualAuth(true);
} catch (PrivilegedActionException pve) {
LOGGER.error("privileged action exception, with root cause", pve.getException());