/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.jmeter.protocol.http.control; import java.io.IOException; import java.io.Serializable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; 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.LoginContext; import javax.security.auth.login.LoginException; import org.apache.jmeter.util.JMeterUtils; import org.slf4j.LoggerFactory; import org.slf4j.Logger; /** * Takes in charge Kerberos auth mechanism * @since 2.10 */ public class KerberosManager implements Serializable { private static final long serialVersionUID = 2L; private static final Logger log = LoggerFactory.getLogger(KerberosManager.class); private static final String JAAS_APPLICATION = JMeterUtils.getPropDefault("kerberos_jaas_application", "JMeter"); //$NON-NLS-1$ $NON-NLS-2$ private final ConcurrentMap<String, Future<Subject>> subjects = new ConcurrentHashMap<>(); public KerberosManager() { super(); } void clearSubjects() { subjects.clear(); } public Subject getSubjectForUser(final String username, final String password) { FutureTask<Subject> task = new FutureTask<>(() -> { LoginContext loginCtx; try { loginCtx = new LoginContext(JAAS_APPLICATION, new LoginCallbackHandler(username, password)); loginCtx.login(); return loginCtx.getSubject(); } catch (LoginException e) { log.warn("Could not log in user " + username, e); } return null; }); if(log.isDebugEnabled()) { log.debug("Subject cached:"+subjects.keySet() +" before:"+username); } Future<Subject> subjectFuture = subjects.putIfAbsent(username, task); if (subjectFuture == null) { subjectFuture = task; task.run(); // NOSONAR we just execute method } try { return subjectFuture.get(); } catch (InterruptedException e1) { log.warn("Interrupted while getting subject for " + username, e1); Thread.currentThread().interrupt(); } catch (ExecutionException e1) { log.warn("Execution of getting subject for " + username + " failed", e1); } return null; } // Needs to be package-protected to avoid problem with serialisation tests static class LoginCallbackHandler implements CallbackHandler { private final String password; private final String username; public LoginCallbackHandler(final String username, final String password) { super(); this.username = username; this.password = password; } @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback callback : callbacks) { if (callback instanceof NameCallback && username != null) { NameCallback nc = (NameCallback) callback; nc.setName(username); } else if (callback instanceof PasswordCallback) { PasswordCallback pc = (PasswordCallback) callback; pc.setPassword(password.toCharArray()); } else { throw new UnsupportedCallbackException( callback, "Unrecognized Callback"); //$NON-NLS-1$ } } } } public String getKrb5Conf() { return System.getProperty("java.security.krb5.conf"); //$NON-NLS-1$ } public boolean getKrb5Debug() { return Boolean.getBoolean("java.security.krb5.debug"); //$NON-NLS-1$ } public String getJaasConf() { return System.getProperty("java.security.auth.login.config"); //$NON-NLS-1$ } @Override public String toString() { return "KerberosManager[jaas: " + getJaasConf() + ", krb5: " + getKrb5Conf() + ", debug: " + getKrb5Debug() +"]"; } }