/* * JBoss, Home of Professional Open Source. * Copyright 2014 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.sasl.gssapi; import static javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.REQUIRED; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; 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.jboss.logging.Logger; /** * Utility class for the JAAS based logins. * * @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a> */ public class JaasUtil { private static Logger log = Logger.getLogger(JaasUtil.class); private static final boolean IS_IBM = System.getProperty("java.vendor").contains("IBM"); public static Subject loginClient() throws LoginException { log.debug("loginClient"); return login("jduke", "theduke".toCharArray(), false, null); } public static Subject loginServer(String keyTabFile) throws LoginException { log.debug("loginServer"); return login("sasl/test_server_1", "servicepwd".toCharArray(), true, keyTabFile); } static Subject login(final String userName, final char[] password, final boolean server, final String keyTabFile) throws LoginException { Subject theSubject = new Subject(); CallbackHandler cbh = new UsernamePasswordCBH(userName, password); Configuration config; if (server) { config = createGssProxyConfiguration(userName, keyTabFile); } else { config = createJaasConfiguration(false); } LoginContext lc = new LoginContext("KDC", theSubject, cbh, config); lc.login(); return theSubject; } private static Configuration createJaasConfiguration(final boolean server) { return new Configuration() { @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { if ("KDC".equals(name) == false) { throw new IllegalArgumentException(String.format("Unexpected name '%s'", name)); } AppConfigurationEntry[] entries = new AppConfigurationEntry[1]; Map<String, Object> options = new HashMap<String, Object>(); options.put("debug", "true"); options.put("refreshKrb5Config", "true"); if (IS_IBM) { options.put("noAddress", "true"); options.put("credsType", server ? "acceptor" : "initiator"); entries[0] = new AppConfigurationEntry("com.ibm.security.auth.module.Krb5LoginModule", REQUIRED, options); } else { options.put("storeKey", "true"); options.put("isInitiator", server ? "false" : "true"); entries[0] = new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", REQUIRED, options); } return entries; } }; } private static Configuration createGssProxyConfiguration(final String principal, final String keyTabFile) { return new Configuration() { @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { if ("KDC".equals(name) == false) { throw new IllegalArgumentException(String.format("Unexpected name '%s'", name)); } AppConfigurationEntry[] entries = new AppConfigurationEntry[1]; Map<String, Object> options = new HashMap<String, Object>(); options.put("debug", "true"); options.put("refreshKrb5Config", "true"); options.put("principal", principal); if (IS_IBM) { options.put("useKeytab", keyTabFile); options.put("noAddress", "true"); options.put("credsType", "acceptor"); entries[0] = new AppConfigurationEntry("com.ibm.security.auth.module.Krb5LoginModule", REQUIRED, options); } else { options.put("useKeyTab", "true"); options.put("keyTab", keyTabFile); options.put("doNotPrompt", "true"); options.put("storeKey", "true"); options.put("isInitiator", "false"); entries[0] = new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", REQUIRED, options); } return entries; } }; } private static class UsernamePasswordCBH implements CallbackHandler { /* * Note: We use CallbackHandler implementations like this in test cases as test cases need to run unattended, a true * CallbackHandler implementation should interact directly with the current user to prompt for the username and * password. * * i.e. In a client app NEVER prompt for these values in advance and provide them to a CallbackHandler like this. */ private final String username; private final char[] password; private UsernamePasswordCBH(final String username, final char[] password) { this.username = username; this.password = password; } @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback current : callbacks) { if (current instanceof NameCallback) { NameCallback ncb = (NameCallback) current; ncb.setName(username); } else if (current instanceof PasswordCallback) { PasswordCallback pcb = (PasswordCallback) current; pcb.setPassword(password); } else { throw new UnsupportedCallbackException(current); } } } } }