From 4f5e6d4b2e533cd7d7c73dcc71a6a501a30081c0 Mon Sep 17 00:00:00 2001 From: Darran Lofthouse Date: Fri, 18 Mar 2022 18:16:48 +0000 Subject: [PATCH] [ELY-2319] Delete the deprecated GSSCredentialSecurityFactory implementation. --- auth/client/pom.xml | 4 + auth/util/pom.xml | 16 +- .../security/auth/util/ElytronMessages.java | 35 +- .../util/GSSCredentialSecurityFactory.java | 560 ------------------ wildfly-elytron/pom.xml | 2 + 5 files changed, 24 insertions(+), 593 deletions(-) delete mode 100644 auth/util/src/main/java/org/wildfly/security/auth/util/GSSCredentialSecurityFactory.java diff --git a/auth/client/pom.xml b/auth/client/pom.xml index 36c5bd6a967..728484b00b9 100644 --- a/auth/client/pom.xml +++ b/auth/client/pom.xml @@ -60,6 +60,10 @@ org.wildfly.security wildfly-elytron-mechanism + + org.wildfly.security + wildfly-elytron-mechanism-gssapi + org.wildfly.security wildfly-elytron-password-impl diff --git a/auth/util/pom.xml b/auth/util/pom.xml index fa86bbd5736..b2c48b4d670 100644 --- a/auth/util/pom.xml +++ b/auth/util/pom.xml @@ -40,10 +40,6 @@ org.wildfly.security wildfly-elytron-auth - - org.wildfly.security - wildfly-elytron-base - org.wildfly.security wildfly-elytron-credential @@ -54,13 +50,10 @@ org.wildfly.security - wildfly-elytron-mechanism-gssapi - - - org.wildfly.security - wildfly-elytron-security-manager-action + wildfly-elytron-x500 + org.wildfly.common wildfly-common @@ -90,10 +83,5 @@ junit test - - org.jmockit - jmockit - test - diff --git a/auth/util/src/main/java/org/wildfly/security/auth/util/ElytronMessages.java b/auth/util/src/main/java/org/wildfly/security/auth/util/ElytronMessages.java index 9c374ce17ab..2d187ab0e6c 100644 --- a/auth/util/src/main/java/org/wildfly/security/auth/util/ElytronMessages.java +++ b/auth/util/src/main/java/org/wildfly/security/auth/util/ElytronMessages.java @@ -19,9 +19,6 @@ package org.wildfly.security.auth.util; import java.io.IOException; -import java.security.GeneralSecurityException; - -import javax.security.auth.login.LoginException; import org.jboss.logging.BasicLogger; import org.jboss.logging.Logger; @@ -51,32 +48,32 @@ interface ElytronMessages extends BasicLogger { ElytronMessages log = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security"); - @Message(id = 3, value = "This builder has already been built") - IllegalStateException builderAlreadyBuilt(); + //@Message(id = 3, value = "This builder has already been built") + //IllegalStateException builderAlreadyBuilt(); @Message(id = 1065, value = "Pattern requires a capture group") IllegalArgumentException patternRequiresCaptureGroup(); - @Message(id = 1121, value = "Unable to perform initial JAAS login.") - GeneralSecurityException unableToPerformInitialLogin(@Cause LoginException cause); + //@Message(id = 1121, value = "Unable to perform initial JAAS login.") + //GeneralSecurityException unableToPerformInitialLogin(@Cause LoginException cause); - @Message(id = 1122, value = "No Kerberos principals found.") - GeneralSecurityException noKerberosPrincipalsFound(); + //@Message(id = 1122, value = "No Kerberos principals found.") + //GeneralSecurityException noKerberosPrincipalsFound(); - @Message(id = 1123, value = "Too many Kerberos principals found.") - GeneralSecurityException tooManyKerberosPrincipalsFound(); + //@Message(id = 1123, value = "Too many Kerberos principals found.") + //GeneralSecurityException tooManyKerberosPrincipalsFound(); - @Message(id = 1160, value = "KeyTab [%s] does not exists.") - IOException keyTabDoesNotExists(String keyTab); + //@Message(id = 1160, value = "KeyTab [%s] does not exists.") + //IOException keyTabDoesNotExists(String keyTab); - @Message(id = 1161, value = "No keys for Kerberos principal [%s] was found in KeyTab [%s].") - IOException noKeysForPrincipalInKeyTab(String principal, String keyTab); + //@Message(id = 1161, value = "No keys for Kerberos principal [%s] was found in KeyTab [%s].") + //IOException noKeysForPrincipalInKeyTab(String principal, String keyTab); - @Message(id = 1165, value = "Initial JAAS login skipped as it has failed in last %d seconds") - GeneralSecurityException initialLoginSkipped(long seconds); + //@Message(id = 1165, value = "Initial JAAS login skipped as it has failed in last %d seconds") + //GeneralSecurityException initialLoginSkipped(long seconds); - @Message(id = 3031, value = "Too many KerberosTicket instances in private credentials") - GeneralSecurityException tooManyKerberosTicketsFound(); + //@Message(id = 3031, value = "Too many KerberosTicket instances in private credentials") + //GeneralSecurityException tooManyKerberosTicketsFound(); @Message(id = 17000, value = "Failed to create credential") IOException xmlFailedToCreateCredential(@Cause Throwable cause); diff --git a/auth/util/src/main/java/org/wildfly/security/auth/util/GSSCredentialSecurityFactory.java b/auth/util/src/main/java/org/wildfly/security/auth/util/GSSCredentialSecurityFactory.java deleted file mode 100644 index 0d751766a44..00000000000 --- a/auth/util/src/main/java/org/wildfly/security/auth/util/GSSCredentialSecurityFactory.java +++ /dev/null @@ -1,560 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual contributors - * as indicated by the @author tags. - * - * 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. - */ -package org.wildfly.security.auth.util; - -import static javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.REQUIRED; -import static org.wildfly.common.Assert.checkNotNullParam; -import static org.wildfly.security.auth.util.ElytronMessages.log; - -import java.io.File; -import java.io.IOException; -import java.security.AccessController; -import java.security.GeneralSecurityException; -import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.UnaryOperator; - -import javax.security.auth.RefreshFailedException; -import javax.security.auth.Subject; -import javax.security.auth.kerberos.KerberosPrincipal; -import javax.security.auth.kerberos.KerberosTicket; -import javax.security.auth.kerberos.KeyTab; -import javax.security.auth.login.AppConfigurationEntry; -import javax.security.auth.login.Configuration; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -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 org.wildfly.common.function.ExceptionSupplier; -import org.wildfly.security.SecurityFactory; -import org.wildfly.security.auth.callback.FastUnsupportedCallbackException; -import org.wildfly.security.credential.GSSKerberosCredential; -import org.wildfly.security.manager.action.SetContextClassLoaderAction; - -/** - * A {@link SecurityFactory} implementation for obtaining a {@link GSSCredential}. - * - * @author Darran Lofthouse - * @deprecated Use {@link org.wildfly.security.mechanism.gssapi.GSSCredentialSecurityFactory} instead - */ -@Deprecated -public final class GSSCredentialSecurityFactory implements SecurityFactory { - - private static final String KRB5LoginModule = "com.sun.security.auth.module.Krb5LoginModule"; - private static final long ONE_SECOND = 1000; - - public static final Oid KERBEROS_V5; - public static final Oid SPNEGO; - - static { - try { - KERBEROS_V5 = new Oid("1.2.840.113554.1.2.2"); - SPNEGO = new Oid("1.3.6.1.5.5.2"); - } catch (GSSException e) { - throw new RuntimeException("Unable to initialise Oid", e); - } - } - - private final int minimumRemainingLifetime; - private final ExceptionSupplier rawSupplier; - - private final AtomicReference cachedCredentialReference = new AtomicReference<>(); - private final UnaryOperator credentialOperator; - - - GSSCredentialSecurityFactory(final int minimumRemainingLifetime, final ExceptionSupplier rawSupplier) { - this.minimumRemainingLifetime = minimumRemainingLifetime; - this.rawSupplier = rawSupplier; - credentialOperator = this::update; - } - - private GSSKerberosCredential update(GSSKerberosCredential original) { - GSSKerberosCredential result = null; - try { - if (original != null) { - if (testIsValid(original.getGssCredential()) && testIsValid(original.getKerberosTicket())) { - result = original; - } - } - - if (result == null) { - log.trace("No valid cached credential, obtaining new one..."); - result = rawSupplier.get(); - log.tracef("Obtained GSSCredentialCredential [%s]", result); - } else { - log.tracef("Used cached GSSCredential [%s]", result); - } - } catch (GeneralSecurityException e) { - throw new IllegalStateException(e); - } - - return result; - } - - private boolean testIsValid(GSSCredential gssCredential) throws GeneralSecurityException { - checkNotNullParam("gssCredential", gssCredential); - boolean stillValid; - try { - int remainingLifetime = gssCredential.getRemainingLifetime(); - log.tracef("Remaining GSSCredential Lifetime = %d", remainingLifetime); - stillValid = remainingLifetime >= minimumRemainingLifetime; - } catch (GSSException e) { - throw new GeneralSecurityException(e); - } - - log.tracef("testIsValid(GSSCredential)=%b", stillValid); - return stillValid; - } - - private boolean testIsValid(KerberosTicket ticket) { - if (ticket == null) { - log.trace("No cached KerberosTicket"); - return true; // If there is no ticket it is not "invalid". - } - - Date endTime = ticket.getEndTime(); - log.tracef("KerberosTicket.getEndTime()=%s", endTime); - boolean stillValid = endTime != null && System.currentTimeMillis() < endTime.getTime() - (minimumRemainingLifetime * ONE_SECOND); - - if (!stillValid) { - log.trace("Attempting to refresh existing KerberosTicket."); - try { - ticket.refresh(); - log.tracef("KerberosTicket refreshed until %s", ticket.getEndTime()); - stillValid = true; - } catch (RefreshFailedException e) { - log.tracef("Unable to refresh KerberosTicket.", e); - } - } - - log.tracef("testIsValid(KerberosTicket)=%b", stillValid); - return stillValid; - } - - @Override - public GSSKerberosCredential create() throws GeneralSecurityException { - try { - return cachedCredentialReference.updateAndGet(credentialOperator); - } catch (RuntimeException e) { - if (e.getCause() instanceof GSSException) { - throw new GeneralSecurityException(e.getCause()); - } else if (e.getCause() instanceof GeneralSecurityException) { - throw (GeneralSecurityException) e.getCause(); - } - - throw e; - } - } - - - /** - * Obtain a new {@link Builder} capable of building a {@link GSSCredentialSecurityFactory}. - * - * @return a new {@link Builder} capable of building a {@link GSSCredentialSecurityFactory}. - */ - public static Builder builder() { - return new Builder(); - } - - /** - * A builder for GSS credential security factories. - */ - public static class Builder { - - private boolean built = false; - private List mechanismOids = new ArrayList<>(); - private String principal; - private File keyTab; - private boolean isServer; - private boolean obtainKerberosTicket; - private int minimumRemainingLifetime; - private int requestLifetime; - private boolean debug; - private boolean wrapGssCredential; - private boolean checkKeyTab; - private volatile long lastFailTime = 0; - private long failCache = 0; - private Map options; - - Builder() { - } - - /** - * Set the keytab file to obtain the identity. - * - * @param keyTab the keytab file to obtain the identity. - * @return {@code this} to allow chaining. - */ - public Builder setKeyTab(final File keyTab) { - assertNotBuilt(); - this.keyTab = keyTab; - - return this; - } - - /** - * Set if the credential returned from the factory is representing the server side of the connection. - * - * @param isServer is the credential returned from the factory is representing the server side of the connection. - * @return {@code this} to allow chaining. - */ - public Builder setIsServer(final boolean isServer) { - assertNotBuilt(); - this.isServer = isServer; - - return this; - } - - /** - * Set if the KerberosTicket should also be obtained and associated with the Credential/ - * - * @param obtainKerberosTicket if the KerberosTicket should also be obtained and associated with the Credential/ - * @return {@code this} to allow chaining. - */ - public Builder setObtainKerberosTicket(final boolean obtainKerberosTicket) { - assertNotBuilt(); - this.obtainKerberosTicket = obtainKerberosTicket; - - return this; - } - - /** - * Once the factory has been called once it will cache the resulting {@link GSSCredential}, this setting - * defines how much life it must have left in seconds for it to be re-used. - * - * @param minimumRemainingLifetime the time in seconds of life a {@link GSSCredential} must have to be re-used. - * @return {@code this} to allow chaining. - */ - public Builder setMinimumRemainingLifetime(final int minimumRemainingLifetime) { - assertNotBuilt(); - this.minimumRemainingLifetime = minimumRemainingLifetime; - - return this; - } - - /** - * Set the lifetime to request newly created credentials are valid for. - * - * @param requestLifetime the lifetime to request newly created credentials are valid for. - * @return {@code this} to allow chaining. - */ - public Builder setRequestLifetime(final int requestLifetime) { - assertNotBuilt(); - this.requestLifetime = requestLifetime < 0 ? GSSCredential.INDEFINITE_LIFETIME : requestLifetime; - - return this; - } - - /** - * Add an {@link Oid} for a mechanism the {@link GSSCredential} should be usable with. - * - * @param oid the {@link Oid} for the mechanism the {@link GSSCredential} should be usable with. - * @return {@code this} to allow chaining. - */ - public Builder addMechanismOid(final Oid oid) { - assertNotBuilt(); - mechanismOids.add(checkNotNullParam("oid", oid)); - - return this; - } - - /** - * Set the principal name for the initial authentication from the KeyTab. - * - * @param principal the principal name for the initial authentication from the KeyTab. - * @return {@code this} to allow chaining. - */ - public Builder setPrincipal(final String principal) { - assertNotBuilt(); - this.principal = principal; - - return this; - } - - /** - * Set if debug logging should be enabled for the JAAS authentication portion of obtaining the {@link GSSCredential} - * - * @param debug if debug logging should be enabled for the JAAS authentication portion of obtaining the {@link GSSCredential} - * @return {@code this} to allow chaining. - */ - public Builder setDebug(final boolean debug) { - assertNotBuilt(); - this.debug = debug; - - return this; - } - - /** - * Set if the constructed {@link GSSCredential} should be wrapped to prevent improper credential disposal or not. - * - * @param value {@code true} if the constructed {@link GSSCredential} should be wrapped; {@code false} otherwise. - * @return {@code this} to allow chaining. - */ - public Builder setWrapGssCredential(final boolean value) { - assertNotBuilt(); - this.wrapGssCredential = value; - - return this; - } - - /** - * Set if keytab file existence and principal presence in it should be checked on factory build. - * - * @param value {@code true} if keytab file should be checked; {@code false} otherwise. - * @return {@code this} to allow chaining. - */ - public Builder setCheckKeyTab(final boolean value) { - assertNotBuilt(); - this.checkKeyTab = value; - - return this; - } - - /** - * Set other configuration options for {@code Krb5LoginModule} - * - * @param options the configuration options which will be appended to options passed into {@code Krb5LoginModule} - * @return {@code this} to allow chaining. - */ - public Builder setOptions(final Map options) { - assertNotBuilt(); - this.options = options; - - return this; - } - - /** - * Set amount of seconds before new try to obtain {@link GSSCredential} should be done if it has failed last time. - * Allows to prevent long waiting to unavailable KDC on every authentication. - * - * @param seconds amount of seconds to cache fail state of the credential factory; 0 if the cache should not be used. - * @return {@code this} to allow chaining. - */ - public Builder setFailCache(final long seconds) { - assertNotBuilt(); - this.failCache = seconds; - - return this; - } - - /** - * Construct a new {@link GSSKerberosCredential} security factory instance. - * - * @return the built factory instance - * @throws IOException when unable to use given KeyTab - */ - public SecurityFactory build() throws IOException { - assertNotBuilt(); - if (checkKeyTab) { - checkKeyTab(); - } - - final Configuration configuration = createConfiguration(); - - built = true; - return new GSSCredentialSecurityFactory(minimumRemainingLifetime > 0 ? minimumRemainingLifetime : 0, () -> createGSSCredential(configuration)); - } - - private GSSKerberosCredential createGSSCredential(Configuration configuration) throws GeneralSecurityException { - if (failCache != 0 && System.currentTimeMillis() - lastFailTime < failCache * 1000) { - throw log.initialLoginSkipped(failCache); - } - - final Subject subject = new Subject(); - - try { - final ClassLoader oldCl = doPrivileged(new SetContextClassLoaderAction(Builder.class.getClassLoader())); - final LoginContext lc; - try { - lc = new LoginContext("KDC", subject, (c) -> { - throw new FastUnsupportedCallbackException(c[0]); - }, configuration); - } finally { - doPrivileged(new SetContextClassLoaderAction(oldCl)); - } - log.tracef("Logging in using LoginContext and subject [%s]", subject); - lc.login(); - log.tracef("Logging in using LoginContext and subject [%s] succeed", subject); - - final KerberosTicket kerberosTicket; - if (obtainKerberosTicket) { - Set kerberosTickets = doPrivileged((PrivilegedAction>) () -> subject.getPrivateCredentials(KerberosTicket.class)); - if (kerberosTickets.size() > 1) { - throw log.tooManyKerberosTicketsFound(); - } - kerberosTicket = kerberosTickets.size() == 1 ? kerberosTickets.iterator().next() : null; - } else { - kerberosTicket = null; - } - - final GSSManager manager = GSSManager.getInstance(); - return Subject.doAs(subject, (PrivilegedExceptionAction) () -> { - Set principals = subject.getPrincipals(KerberosPrincipal.class); - if (principals.size() < 1) { - throw log.noKerberosPrincipalsFound(); - } else if (principals.size() > 1) { - throw log.tooManyKerberosPrincipalsFound(); - } - KerberosPrincipal principal = principals.iterator().next(); - log.tracef("Creating GSSName for Principal '%s'", principal); - GSSName name = manager.createName(principal.getName(), GSSName.NT_USER_NAME, KERBEROS_V5); - - if (wrapGssCredential) { - return new GSSKerberosCredential(wrapCredential(manager.createCredential(name, requestLifetime, mechanismOids.toArray(new Oid[mechanismOids.size()]), - isServer ? GSSCredential.ACCEPT_ONLY : GSSCredential.INITIATE_ONLY)), kerberosTicket); - } - return new GSSKerberosCredential(manager.createCredential(name, requestLifetime, mechanismOids.toArray(new Oid[mechanismOids.size()]), - isServer ? GSSCredential.ACCEPT_ONLY : GSSCredential.INITIATE_ONLY), kerberosTicket); - }); - - } catch (LoginException e) { - if (failCache != 0) { - lastFailTime = System.currentTimeMillis(); - } - throw log.unableToPerformInitialLogin(e); - } catch (PrivilegedActionException e) { - if (e.getCause() instanceof GeneralSecurityException) { - throw (GeneralSecurityException) e.getCause(); - } - throw new GeneralSecurityException(e.getCause()); - } - } - - private static T doPrivileged(final PrivilegedAction action) { - return System.getSecurityManager() != null ? AccessController.doPrivileged(action) : action.run(); - } - - private void checkKeyTab() throws IOException { - KeyTab kt = KeyTab.getInstance(keyTab); - if (!kt.exists()) { - throw log.keyTabDoesNotExists(keyTab.getAbsolutePath()); - } - if (kt.getKeys(new KerberosPrincipal(principal)).length == 0) { - throw log.noKeysForPrincipalInKeyTab(principal, keyTab.getAbsolutePath()); - } - } - - private Configuration createConfiguration() throws IOException { - Map options = new HashMap<>(); - if (debug) { - options.put("debug", "true"); - } - options.put("principal", principal); - - options.put("storeKey", "true"); - options.put("useKeyTab", "true"); - if (keyTab != null) options.put("keyTab", keyTab.getAbsolutePath()); - options.put("isInitiator", (isServer && !obtainKerberosTicket) ? "false" : "true"); - - if (this.options != null) { - options.putAll(this.options); - } - - log.tracef("Created LoginContext configuration: %s", options.toString()); - - final AppConfigurationEntry[] aceArray = new AppConfigurationEntry[] { - new AppConfigurationEntry(KRB5LoginModule, REQUIRED, options) - }; - - return new Configuration() { - - @Override - public AppConfigurationEntry[] getAppConfigurationEntry(String name) { - assert "KDC".equals(name); - return aceArray; - } - - }; - } - - private void assertNotBuilt() { - if (built) { - throw log.builderAlreadyBuilt(); - } - } - - } - - private static GSSCredential wrapCredential(final GSSCredential credential) { - return new GSSCredential() { - - @Override - public int getUsage(Oid mech) throws GSSException { - return credential.getUsage(mech); - } - - @Override - public int getUsage() throws GSSException { - return credential.getUsage(); - } - - @Override - public int getRemainingLifetime() throws GSSException { - return credential.getRemainingLifetime(); - } - - @Override - public int getRemainingInitLifetime(Oid mech) throws GSSException { - return credential.getRemainingInitLifetime(mech); - } - - @Override - public int getRemainingAcceptLifetime(Oid mech) throws GSSException { - return credential.getRemainingAcceptLifetime(mech); - } - - @Override - public GSSName getName(Oid mech) throws GSSException { - return credential.getName(mech); - } - - @Override - public GSSName getName() throws GSSException { - return credential.getName(); - } - - @Override - public Oid[] getMechs() throws GSSException { - return credential.getMechs(); - } - - @Override - public void dispose() throws GSSException { - // Prevent disposal of our credential. - } - - @Override - public void add(GSSName name, int initLifetime, int acceptLifetime, Oid mech, int usage) throws GSSException { - credential.add(name, initLifetime, acceptLifetime, mech, usage); - } - - }; - } -} diff --git a/wildfly-elytron/pom.xml b/wildfly-elytron/pom.xml index 1869b3bf5c7..af5c473b626 100644 --- a/wildfly-elytron/pom.xml +++ b/wildfly-elytron/pom.xml @@ -945,6 +945,8 @@ org.wildfly.security.auth.client.ElytronMessages_$logger + org.wildfly.security.auth.util.ElytronMessages_$logger + org.wildfly.security.auth.util.GSSCredentialSecurityFactory true