package org.ovirt.engine.core.login; import java.security.AccessController; import java.security.Principal; import java.security.PrivilegedAction; import java.security.acl.Group; import java.util.Map; import javax.resource.spi.security.PasswordCredential; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.LoginException; import org.jboss.resource.security.AbstractPasswordCredentialLoginModule; import org.jboss.security.SimplePrincipal; import org.ovirt.engine.core.compat.LogCompat; import org.ovirt.engine.core.compat.LogFactoryCompat; import org.ovirt.engine.core.engineencryptutils.EncryptionUtils; /** * An example of how one could use this custom login class other than the JBoss default class * {@code org.jboss.resource.security.SecureIdentityLoginModule} which this class is based on:<br> * <li>Add full path to the jar which contains this class to <i>$JBOSS_HOME/$PROFILE/conf/bootstrap/security.xml</i> as * an entry of element <classloader name="security-classloader"...><br> * e.g.: * <pre> * <root>${jboss.server.home.url}deploy/engine.ear/lib/engine-login-3.0.0-0001.jar</root> * </pre> * <li>Modify <i>$JBOSS_HOME/$PROFILE/conf/login-config.xml</i> to use the custom login class in element * <application-policy name="EncryptDBPassword"> (a complete element is shown below) e.g.: * <pre> * <code> * <!--login-module code="org.jboss.resource.security.SecureIdentityLoginModule" flag="required"--> * <login-module code="org.ovirt.engine.core.login.EngineSecureIdentityLoginModule" flag="required"> * </pre> * </code> <li>Encrypt the database password for a jca connection factory and set it in <i>EncryptDBPassword</i> * application-policy element: * <code> * <pre> * <application-policy name="EncryptDBPassword"> * <authentication> * <!--login-module code="org.jboss.resource.security.SecureIdentityLoginModule" flag="required"--> * <login-module code="org.ovirt.engine.core.security.login.EngineSecureIdentityLoginModule" flag="required"> * <module-option name="username">sa</module-option> * <module-option name="password">-1ef77a3433f8ba8aa370e115b1e73a8b</module-option> * <module-option name="managedConnectionFactoryName">jboss.jca:name=ENGINEDataSource,service=LocalTxCM</module-option> * </login-module> * </authentication> * </application-policy> * </pre> * </code> * <li>{@link #decode()} responsible for decoding the password by a customized algorithm as provided by {@link * EncryptionUtils.#decode(String, String, String)}<br> * or using the default if not specified any. * <li>The default value for cipher algorithm is Blowfish, and key derived from the phrase 'jaas is the way'.<br> * However, it is designed to support additional algorithms and keys. The full list of supported algorithms is <br> * specified in the following link. * @link<a href="http://download.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html">Java * Cryptography Algorithms</a> */ public class EngineSecureIdentityLoginModule extends AbstractPasswordCredentialLoginModule { private static final LogCompat log = LogFactoryCompat.getLog(EngineSecureIdentityLoginModule.class); private String username; private String password; public void initialize(Subject subject, CallbackHandler handler, Map sharedState, Map options) { super.initialize(subject, handler, sharedState, options); username = (String) options.get("username"); if (username == null) { username = (String) options.get("userName"); if (username == null) { throw new IllegalArgumentException("The user name is a required option"); } } password = (String) options.get("password"); if (password == null) { throw new IllegalArgumentException("The password is a required option"); } } public boolean login() throws LoginException { log.trace("login called"); if (super.login() == true) return true; super.loginOk = true; return true; } public boolean commit() throws LoginException { Principal principal = new SimplePrincipal(username); AccessController.doPrivileged(new AddPrincipalsAction(subject, principal)); sharedState.put("javax.security.auth.login.name", username); // Decode the encrypted password try { char[] decodedPassword = decode(password); PasswordCredential cred = new PasswordCredential(username, decodedPassword); cred.setManagedConnectionFactory(getMcf()); AccessController.doPrivileged(new AddCredentialsAction(subject, cred)); } catch (Exception e) { log.debug("Failed to decode password", e); throw new LoginException("Failed to decode password: " + e.getMessage()); } return true; } public boolean abort() { username = null; password = null; return true; } protected Principal getIdentity() { log.trace("getIdentity called, username=" + username); Principal principal = new SimplePrincipal(username); return principal; } protected Group[] getRoleSets() throws LoginException { Group[] empty = new Group[0]; return empty; } private static String encode(String secret, String keyMaterial, String algorithm) { return EncryptionUtils.encode(secret, keyMaterial, algorithm); } /** * responsible for decoding the password by a customized algorithm as provided by {@link * EncryptionUtils.decode(password, key, algorithm)}. Customizing algorithm and key material could be achieved by * supplying other than null values to {@link EncryptionUtils.decode(password, key, algorithm)} */ private static char[] decode(String secret) { String decode = EncryptionUtils.decode(secret, null, null); return decode != null ? decode.toCharArray() : null; } static class AddPrincipalsAction implements PrivilegedAction<Object> { Subject subject; Principal p; AddPrincipalsAction(Subject subject, Principal p) { this.subject = subject; this.p = p; } public Object run() { subject.getPrincipals().add(p); return null; } } static class AddCredentialsAction implements PrivilegedAction<Object> { Subject subject; PasswordCredential cred; AddCredentialsAction(Subject subject, PasswordCredential cred) { this.subject = subject; this.cred = cred; } public Object run() { subject.getPrivateCredentials().add(cred); return null; } } /** * Main entry point to encrypt a password using the hard-coded pass phrase * @param args * <br> * - [0] = the password to encode<br> * - [1] = the key material for encoding<br> * - [2] = the algorithm for encoding * @throws Exception */ public static void main(String[] args) throws Exception { String encode = null; switch (args.length) { case 1: encode = encode(args[0], null, null); System.out.println("Encoded password: " + encode); break; case 3: encode = encode(args[0], args[1], args[2]); System.out.println("Encoded password: " + encode); break; default: System.out.println("Usage: <password> [<key material> <algorithm>]"); break; } } }