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

com.hazelcast.security.impl.SecurityUtil Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
 *
 * 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 com.hazelcast.security.impl;

import com.hazelcast.config.LoginModuleConfig;
import com.hazelcast.config.LoginModuleConfig.LoginModuleUsage;
import com.hazelcast.config.security.JaasAuthenticationConfig;
import com.hazelcast.config.security.RealmConfig;
import com.hazelcast.logging.ILogger;
import com.hazelcast.logging.Logger;
import com.hazelcast.security.RealmConfigCallback;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.io.File;
import java.io.IOException;

/**
 * Helper methods related to Hazelcast security routines.
 */
@SuppressWarnings({"checkstyle:classdataabstractioncoupling", "checkstyle:ClassFanOutComplexity"})
public final class SecurityUtil {

    private static final String TEMP_LOGIN_CONTEXT_NAME = "realmConfigLogin";
    private static final String FQCN_KRB5LOGINMODULE_SUN = "com.sun.security.auth.module.Krb5LoginModule";
    private static final String FQCN_KRB5LOGINMODULE_IBM = "com.ibm.security.auth.module.Krb5LoginModule";

    private static final ILogger LOGGER = Logger.getLogger(SecurityUtil.class);

    private static final ThreadLocal SECURE_CALL = new ThreadLocal<>();

    private SecurityUtil() {
    }

    /**
     * Runs JAAS authentication ({@link LoginContext#login()}) on {@link RealmConfig} with given name retrieved by using given
     * {@link CallbackHandler}. Return either the authenticated {@link Subject} when the authentication passes or {@code null}.
     *
     * @param callbackHandler handler used to retrieve the {@link RealmConfig}
     * @param securityRealm   name of a security realm to be retrieved by callbackHandler
     * @return {@link Subject} when the authentication passes, {@code null} otherwise
     */
    public static Subject getRunAsSubject(CallbackHandler callbackHandler, String securityRealm) {
        if (securityRealm == null) {
            if (LOGGER.isFineEnabled()) {
                LOGGER.fine("No RunAs Subject created for callbackHandler=" + callbackHandler + ", realm is not provided");
            }
            return null;
        }
        RealmConfigCallback cb = new RealmConfigCallback(securityRealm);
        try {
            callbackHandler.handle(new Callback[]{cb});
        } catch (IOException | UnsupportedCallbackException e) {
            LOGGER.info("Unable to retrieve the RealmConfig", e);
            return null;
        }
        return getRunAsSubject(callbackHandler, cb.getRealmConfig());
    }

    /**
     * Runs JAAS authentication ({@link LoginContext#login()}) on given {@link RealmConfig}.
     * Return either the authenticated {@link Subject} when the authentication passes or {@code null}.
     *
     * @param callbackHandler handler used to retrieve the {@link RealmConfig}
     * @return {@link Subject} when the authentication passes, {@code null} otherwise
     */
    public static Subject getRunAsSubject(CallbackHandler callbackHandler, RealmConfig realmConfig) {
        if (realmConfig == null) {
            if (LOGGER.isFineEnabled()) {
                LOGGER.fine("The realmConfig is not provided.");
            }
            return null;
        }
        LoginConfigurationDelegate loginConfiguration = new LoginConfigurationDelegate(realmConfig.asLoginModuleConfigs());
        try {
            LoginContext lc = new LoginContext(TEMP_LOGIN_CONTEXT_NAME, new Subject(), callbackHandler, loginConfiguration);
            lc.login();
            return lc.getSubject();
        } catch (LoginException e) {
            LOGGER.info("Authentication failed.", e);
            return null;
        }
    }

    public static RealmConfig createKerberosJaasRealmConfig(String principal, String keytabPath, boolean isInitiator) {
        if (keytabPath == null) {
            if (LOGGER.isFineEnabled()) {
                LOGGER.fine("The keytab path is not provided.");
            }
            return null;
        }
        LoginModuleConfig krb5LoginModuleConfig;
        if (hasLoginModuleClass(FQCN_KRB5LOGINMODULE_SUN)) {
            krb5LoginModuleConfig = new LoginModuleConfig(FQCN_KRB5LOGINMODULE_SUN, LoginModuleUsage.REQUIRED)
                    .setOrClear("keyTab", keytabPath).setProperty("doNotPrompt", "true")
                    .setProperty("useKeyTab", "true")
                    .setProperty("storeKey", "true")
                    .setProperty("isInitiator", Boolean.toString(isInitiator));
        } else if (hasLoginModuleClass(FQCN_KRB5LOGINMODULE_IBM)) {
            krb5LoginModuleConfig = new LoginModuleConfig(FQCN_KRB5LOGINMODULE_IBM, LoginModuleUsage.REQUIRED)
                    .setProperty("useKeytab", new File(keytabPath).toURI().toString())
                    .setProperty("credsType", isInitiator ? "both" : "acceptor");
        } else {
            throw new UnsupportedOperationException("No supported Krb5LoginModule was found in the current Java runtime."
                    + " The JAAS security realm configurations can't be created automatically."
                    + " You have to explicitly configure the realms.");
        }
        krb5LoginModuleConfig
                .setOrClear("principal", principal)
                .setProperty("refreshKrb5Config", "true");
        RealmConfig kerberosRealmConfig = new RealmConfig()
                .setJaasAuthenticationConfig(new JaasAuthenticationConfig().addLoginModuleConfig(krb5LoginModuleConfig));
        if (LOGGER.isFineEnabled()) {
            LOGGER.fine(
                    "A helper security realm for Kerberos keytab-based authentication was generated: " + kerberosRealmConfig);
        }
        return kerberosRealmConfig;
    }

    private static boolean hasLoginModuleClass(String fcqn) {
        try {
            Class.forName(fcqn);
            return true;
        } catch (Throwable t) {
            if (LOGGER.isFinestEnabled()) {
                LOGGER.finest("Login module class not found: " + fcqn, t);
            }
        }
        return false;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy