Java tutorial
/* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. 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.wso2.carbon.identity.application.authenticator.iwa; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSManager; import org.wso2.carbon.base.CarbonBaseConstants; import org.wso2.carbon.identity.application.authenticator.iwa.internal.IWAServiceDataHolder; import org.wso2.carbon.identity.application.common.model.ClaimMapping; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.user.core.claim.Claim; import sun.security.jgss.GSSUtil; import java.nio.file.Paths; import java.security.Principal; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.HashMap; import java.util.Map; import java.util.Set; 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.kerberos.KerberosPrincipal; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import javax.servlet.http.HttpServletRequest; /** * Util class for IWA Authenticator */ public class IWAAuthenticationUtil { private static GSSManager gssManager = GSSManager.getInstance(); private static IWAServiceDataHolder dataHolder = IWAServiceDataHolder.getInstance(); // holds the local IWA Authenticator credentials private static transient GSSCredential localIWACredentials; private static transient KerberosPrincipal serverPrincipal; private static Log log = LogFactory.getLog(IWAAuthenticationUtil.class); /** * Process kerberos token and get user name * * @param gssToken kerberos token * @return username Username of the logged in user * @throws GSSException */ public static String processToken(byte[] gssToken, GSSCredential gssCredentials) throws GSSException { GSSContext context = gssManager.createContext(gssCredentials); // decrypt the kerberos ticket (GSS token) context.acceptSecContext(gssToken, 0, gssToken.length); // if we cannot decrypt the GSS Token we return the username as null if (!context.isEstablished()) { log.error("Unable to decrypt the kerberos ticket as context was not established."); return null; } String loggedInUserName = context.getSrcName().toString(); String target = context.getTargName().toString(); if (log.isDebugEnabled()) { String msg = "Extracted details from GSS Token, LoggedIn User : " + loggedInUserName + " , Intended target : " + target; log.debug(msg); } return loggedInUserName; } /** * Process gss token with local IWA credentials * * @param gssToken kerberos token * @return username of the logged in user * @throws GSSException */ public static String processToken(byte[] gssToken) throws GSSException { return processToken(gssToken, localIWACredentials); } /** * Set jaas.conf and krb5 paths */ public static void setConfigFilePaths() { String jaasConfigPath = System.getProperty(IWAConstants.JAAS_CONFIG_FILE); String carbonHome = System.getProperty(CarbonBaseConstants.CARBON_HOME); // set jaas.conf file path if not set by the system property already if (IdentityUtil.isBlank(jaasConfigPath)) { jaasConfigPath = Paths .get(carbonHome, "repository", "conf", "identity", IWAConstants.JAAS_CONF_FILE_NAME).toString(); System.setProperty(IWAConstants.JAAS_CONFIG_FILE, jaasConfigPath); } if (log.isDebugEnabled()) { log.debug("Kerberos JAAS module config file path set to : " + jaasConfigPath); } } /** * Create server credential using SPNName and SPNPassword. This credential is used to decrypt the Kerberos Token * presented by the user. Although an actual authentication does not happen with the KDC, an invalid password * will result in checksum failure when decrypting the token. * * @param callbackHandler username password callback handler * @throws PrivilegedActionException * @throws LoginException */ private static GSSCredential createServerCredentials(CallbackHandler callbackHandler) throws PrivilegedActionException, LoginException { LoginContext loginContext = new LoginContext(IWAConstants.SERVER, callbackHandler); loginContext.login(); if (log.isDebugEnabled()) { log.debug("Pre-authentication successful for with Kerberos Server."); } // create server credentials from pre authentication with the AD return createCredentialsForSubject(loginContext.getSubject()); } /** * Create GSSCredential as Subject * * @param subject login context subject * @return GSSCredential * @throws PrivilegedActionException */ private static GSSCredential createCredentialsForSubject(final Subject subject) throws PrivilegedActionException { final PrivilegedExceptionAction<GSSCredential> action = new PrivilegedExceptionAction<GSSCredential>() { public GSSCredential run() throws GSSException { return gssManager.createCredential(null, GSSCredential.INDEFINITE_LIFETIME, GSSUtil.GSS_SPNEGO_MECH_OID, GSSCredential.ACCEPT_ONLY); } }; if (log.isDebugEnabled()) { Set<Principal> principals = subject.getPrincipals(); String principalName = null; if (principals != null) { principalName = principals.toString(); } log.debug("Creating gss credentials as principal : " + principalName); } return Subject.doAs(subject, action); } /** * Create call back handler using given username and password * * @param username * @param password * @return CallbackHandler */ private static CallbackHandler getUserNamePasswordCallbackHandler(final String username, final char[] password) { return new CallbackHandler() { public void handle(final Callback[] callback) { for (Callback currentCallBack : callback) { if (currentCallBack instanceof NameCallback) { final NameCallback nameCallback = (NameCallback) currentCallBack; nameCallback.setName(username); } else if (currentCallBack instanceof PasswordCallback) { final PasswordCallback passCallback = (PasswordCallback) currentCallBack; passCallback.setPassword(password); } else { log.error("Unsupported Callback class = " + currentCallBack.getClass().getName()); } } } }; } /** * Create GSSCredentials required to decrypt the Kerberos Token from the Kerberos Server in which Identity Server * is a Service Principal. * * @param spnName Service Principal Name for the Identity Server * @param spnPassword Service Principal password * @return created GSSCredentials * @throws PrivilegedActionException * @throws LoginException * @throws GSSException */ public static GSSCredential createCredentials(String spnName, char[] spnPassword) throws PrivilegedActionException, LoginException, GSSException { CallbackHandler callbackHandler = getUserNamePasswordCallbackHandler(spnName, spnPassword); return createServerCredentials(callbackHandler); } /** * Util Method to extract the Realm (Domain) name from a fullyQualified user name * eg: admin@IS.LOCAL --> IS.LOCAL * * @param fullyQualifiedUserName * @return */ public static String extractRealmFromUserName(String fullyQualifiedUserName) { if (StringUtils.isEmpty(fullyQualifiedUserName)) { throw new IllegalArgumentException("Authenticated user's fully qualified name cannot be empty."); } // remove the AD domain from the username int index = fullyQualifiedUserName.lastIndexOf("@"); return fullyQualifiedUserName.substring(index + 1); } /** * Util method to get the domain/realm aware username from the fullyQualified username * eg: admin@IS.LOCAL --> admin * * @param fullyQualifiedUserName * @return */ public static String getDomainAwareUserName(String fullyQualifiedUserName) { if (StringUtils.isEmpty(fullyQualifiedUserName)) { throw new IllegalArgumentException("Authenticated user's fully qualified name cannot be empty."); } // remove the AD domain from the username int index = fullyQualifiedUserName.lastIndexOf("@"); return fullyQualifiedUserName.substring(0, index); } /** * Invalide a session. This is to prevent session fixation attacks * * @param request */ public static void invalidateSession(HttpServletRequest request) { if (request.isRequestedSessionIdValid()) { // invalidate the session. ie. clear all attributes request.getSession().invalidate(); // create a new session thereby creating a new jSessionID request.getSession(true); } } /** * Build a claim mapping map with the Claim array for set claims. * * @param userClaims * @return */ public static Map<ClaimMapping, String> buildClaimMappingMap(Claim[] userClaims) { Map<ClaimMapping, String> claims = new HashMap<>(); for (Claim iwaClaim : userClaims) { if (iwaClaim.getValue() != null) { claims.put(ClaimMapping.build(iwaClaim.getClaimUri(), iwaClaim.getClaimUri(), iwaClaim.getValue(), false), iwaClaim.getValue()); } } return claims; } }