8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The contents of this file are subject to the terms
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * of the Common Development and Distribution License
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * (the License). You may not use this file except in
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * compliance with the License.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * You can obtain a copy of the License at
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * https://opensso.dev.java.net/public/CDDLv1.0.html or
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * See the License for the specific language governing
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * permission and limitations under the License.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * When distributing Covered Code, include this CDDL
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Header Notice in each file and include the License file
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If applicable, add the following below the CDDL Header,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * with the fields enclosed by brackets [] replaced by
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * your own identifying information:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * "Portions Copyrighted [year] [name of copyright owner]"
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * $Id: SMSAuthModule.java,v 1.9 2009/12/11 06:51:37 hengming Exp $
d5a35ee0e81c6fb618b79309f71177dc876cbd65Neil Madden * Portions Copyrighted 2011-2016 ForgeRock AS.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpackage com.sun.identity.authentication.internal.server;
bee2440354b4bc8796e1de0b6cbd60e1f68deba0Phill Cunningtonimport static org.forgerock.openam.ldap.LDAPUtils.rdnValueFromDn;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.authentication.internal.AuthPrincipal;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.authentication.internal.AuthSubject;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.authentication.internal.LoginModule;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.authentication.spi.InvalidPasswordException;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.authentication.util.ISAuthConstants;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.security.AdminPasswordAction;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.security.AdminTokenAction;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.sm.ServiceConfigManager;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport com.sun.identity.sm.ServiceSchemaManager;
d5a35ee0e81c6fb618b79309f71177dc876cbd65Neil Maddenimport java.util.concurrent.ConcurrentSkipListMap;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport javax.security.auth.callback.CallbackHandler;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport javax.security.auth.callback.NameCallback;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport javax.security.auth.callback.PasswordCallback;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport javax.security.auth.callback.UnsupportedCallbackException;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterimport javax.security.auth.login.LoginException;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * AM's internal user's authentication module
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpublic class SMSAuthModule implements LoginModule {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Static variables
d5a35ee0e81c6fb618b79309f71177dc876cbd65Neil Madden private static volatile boolean initialized = false;
d5a35ee0e81c6fb618b79309f71177dc876cbd65Neil Madden private static volatile boolean loadedInternalUsers = false;
d5a35ee0e81c6fb618b79309f71177dc876cbd65Neil Madden private static volatile boolean registeredCallbackHandler = false;
d5a35ee0e81c6fb618b79309f71177dc876cbd65Neil Madden private static final Map<String, String> users = new ConcurrentSkipListMap<>(String.CASE_INSENSITIVE_ORDER);
d5a35ee0e81c6fb618b79309f71177dc876cbd65Neil Madden private static final Map<String, String> userNameToDN = new ConcurrentSkipListMap<>(String.CASE_INSENSITIVE_ORDER);
d5a35ee0e81c6fb618b79309f71177dc876cbd65Neil Madden private static final Debug debug = Debug.getInstance("amAuthInternalSMModule");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Instance variables
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Constants
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String IDREPO_SERVICE = "sunIdentityRepositoryService";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static final String PASSWORD = "userPassword";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // do nothing
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModule constructor called");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster public void initialize(AuthSubject subject, CallbackHandler cb,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster this.sharedState = (isharedstate != null) ? isharedstate
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster this.options = (ioptions != null) ? ioptions : Collections.EMPTY_MAP;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Copy the shared state and remove password for debugging
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster .remove(ISAuthConstants.SHARED_STATE_PASSWORD) == null)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModule::initialize called "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + "\nPrincipals: "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + ((subject != null) ? subject.getPrincipals().toString()
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + "\nSharedState: "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster : "<Password Absent>") + "\nOptions: " + options);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModule.initialize() Initializing "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + "Username and password from serverconfig.xml");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // reset so that internal users set will be reloaded later in time.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // initialize caches.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Get internal user names and passwords from serverconfig.xml
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // %%% Might have to get them directory from DSConfigMgr %%%
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // if other than "default" needs to be used
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModule.initialize() Username "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Check if the user is already present
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModule::login() From shared state: "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + ((password == null) ? "<not present>" : "<present>"));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Check if we have username and password, if not send callbacks
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Request for both username and password
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster cbs[1] = new PasswordCallback("Password: ", false);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModule::login() Sending "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + "Name & Password Callback");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster char[] passwd = ((PasswordCallback) cbs[1]).getPassword();
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Authenticate the user, return false is username or password is null
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster boolean authenticated = false;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModule::login() For authentication: "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + "Username: " + username + " Password: <present>");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Load the internal users and try to get userDN
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Need to make sure userDN is not null, since this
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // be set in the subject
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Get the hashed password for the user
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster String cachedUserDN = (String) userNameToDN.get(username);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster String normalizedUserDN = DNUtils.normalizeDN(userDN);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModule::login() Invalid User DN");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return false;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Compare the hashed password
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster boolean invalidPassword = false;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (hash != null && hash.equals(Hash.hash(password))) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModule::login() Success AuthN");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Load the internal users and compare hashed passwords
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + "Loading internal users");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster cachedUserDN = (String) userNameToDN.get(username);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster String normalizedUserDN = DNUtils.normalizeDN(userDN);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + "Invalid User DN");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return false;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return false;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (hash != null && hash.equals(Hash.hash(password))) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + "Success AuthN");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Password must be invalid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Password must be invalid
181a73d2546f0674bf1fe178e9ff2b420b83a949Quentin CASTEL debug.message("SMSAuthModule::login() Invalid password for username: {}", username);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster throw (new InvalidPasswordException("invalid password",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // do nothing
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (true);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster public boolean commit() throws LoginException {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // add username to Subject
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModule::commit() Adding Principal: " + userDN
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster + " to Subject");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (true);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster public boolean logout() throws LoginException {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // do nothing
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (true);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static synchronized void loadInternalUsers() {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Get AdminSSOToken
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SSOToken ssoToken = (SSOToken) AccessController
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ServiceConfigManager scm = new ServiceConfigManager(IDREPO_SERVICE,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ServiceSchemaManager ssm = new ServiceSchemaManager(IDREPO_SERVICE,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (Iterator items = sc.getSubConfigNames().iterator(); items
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Add root suffix, if revision is greater than 30
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // In the case of upgrade the DN will have the suffix
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Hence check if it ends with SMS root suffix
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Replace only if the they are different
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModule::loadInternalUsers() "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Setup listeners
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Handle the exception
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster private static void addUserToCache(String name, String hash) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Add the DN
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Add the "name"
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Inner class for receiving SMS notifications
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster static class SMSAuthModuleListener implements ServiceListener {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Do nothing
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModuleListener::init called");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster public void schemaChanged(String serviceName, String version) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModuleListener::schemaChanged called");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster public void globalConfigChanged(String serviceName, String version,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster String groupName, String serviceComponent, int type) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModuleListener::globalConfigChanged");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (serviceName.equalsIgnoreCase(IDREPO_SERVICE)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster // Force the loading of internal users
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster public void organizationConfigChanged(String serviceName,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster String version, String orgName, String groupName,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster debug.message("SMSAuthModuleListener::orgConfigChanged");