/*
* JBoss, Home of Professional Open Source.
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.teiid.jboss;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.acl.Group;
import java.util.HashMap;
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.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import org.jboss.security.SecurityContextAssociation;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.vault.SecurityVaultException;
import org.jboss.security.vault.SecurityVaultUtil;
import org.picketbox.datasource.security.AbstractPasswordCredentialLoginModule;
import org.teiid.OAuthCredentialContext;
/**
* A simple login module passes the principal making the connection request
* to EIS, as pass-through without any validation.
*
*/
@SuppressWarnings("unchecked")
public class PassthroughIdentityLoginModule extends AbstractPasswordCredentialLoginModule {
/**
* Module option to specify if any {@link GSSCredential} being added to the
* {@link Subject} should be wrapped to prevent disposal.
*
* Has no effect if a {@link GSSCredential} is not being added to the
* {@link Subject}.
*
* Defaults to false.
*/
private static final String WRAP_GSS_CREDENTIAL = "wrapGSSCredential";
private String userName;
private char[] password;
private Subject callerSubject;
private boolean addPrincipal = true;
private HashMap<String, Object> properties = new HashMap<String, Object>();
private boolean wrapGssCredential;
private Subject intermediateSubject;
private GSSCredential storedCredential;
@Override
public void initialize(Subject subject, CallbackHandler handler, Map<String, ?> sharedState, Map<String, ?> options) {
super.initialize(subject, handler, sharedState, options);
this.userName = (String) options.get("username"); //$NON-NLS-1$
String pass = (String) options.get("password");//$NON-NLS-1$
if (pass != null) {
if (SecurityVaultUtil.isVaultFormat(pass)) {
try {
pass = SecurityVaultUtil.getValueAsString(pass);
} catch (SecurityVaultException e) {
throw new RuntimeException(e);
}
this.password = pass.toCharArray();
} else {
this.password = pass.toCharArray();
}
}
this.properties.putAll(options);
this.wrapGssCredential = Boolean.parseBoolean((String) options.get(WRAP_GSS_CREDENTIAL));
log.tracef("wrapGssCredential=%b", wrapGssCredential);
}
@Override
public boolean login() throws LoginException {
String username = userName;
try {
Principal user = getPrincipal();
this.callerSubject = getSubject();
this.addPrincipal = false;
if (user != null) {
username = user.getName();
}
} catch (Throwable e) {
throw new LoginException(e.getMessage());
}
// Update userName so that getIdentity is consistent
this.userName = username;
if (super.login() == true) {
return true;
}
// Put the principal name into the sharedState map
sharedState.put("javax.security.auth.login.name", username); //$NON-NLS-1$
super.loginOk = true;
return true;
}
@Override
public boolean commit() throws LoginException {
// Put the principal name into the sharedState map
sharedState.put("javax.security.auth.login.name", userName); //$NON-NLS-1$
if (this.addPrincipal) {
subject.getPrincipals().add(getIdentity());
// Add the PasswordCredential
if (this.password != null) {
PasswordCredential cred = new PasswordCredential(userName, password);
SecurityActions.addCredentials(subject, cred);
}
}
if (this.callerSubject != null) {
GSSCredential rawCredential = getGssCredential(this.callerSubject);
if (rawCredential != null) {
log.trace("Kerberos passthough mechanism in works");
this.storedCredential = wrapGssCredential ? wrapCredential(rawCredential) : rawCredential;
this.intermediateSubject = GSSUtil.createGssSubject(rawCredential, storedCredential);
if (this.intermediateSubject == null){
throw new LoginException(IntegrationPlugin.Util.gs(IntegrationPlugin.Event.TEIID50108));
}
log.tracef("created a subject from deletegate credential");
makeCopy(intermediateSubject, this.subject);
log.tracef("Copied contents of temporary Subject to Subject from the LoginContext");
addPrivateCredential(this.subject, storedCredential);
log.trace("Also add the GSSCredential to the Subject");
} else {
makeCopy(this.callerSubject, this.subject);
}
}
addPrivateCredential(this.subject, this.properties);
log.trace("Adding module option properties as private credential");
// if oauth credential available in calling context then add the OAuthCredential.
if (OAuthCredentialContext.getCredential() != null) {
addPrivateCredential(this.subject, OAuthCredentialContext.getCredential());
log.trace("Adding OAuth credential as private credential");
}
return true;
}
@Override
public boolean logout() throws LoginException {
if (System.getSecurityManager() == null) {
if (storedCredential != null) {
removePrivateCredential(subject, storedCredential);
log.trace("Remove GSSCredential to the Subject");
}
removePrivateCredential(subject, properties);
clearSubjectContents(subject, intermediateSubject != null?intermediateSubject:callerSubject);
log.trace("Clear Subject contents");
return true;
}
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
if (storedCredential != null) {
removePrivateCredential(subject, storedCredential);
log.trace("Remove GSSCredential to the Subject");
}
removePrivateCredential(subject, properties);
clearSubjectContents(subject, intermediateSubject != null?intermediateSubject:callerSubject);
log.trace("Clear Subject contents");
return true;
}
});
}
private GSSCredential getGssCredential(Subject subject) {
for(Object obj:subject.getPrivateCredentials()) {
if (obj instanceof GSSCredential) {
return (GSSCredential)obj;
}
}
return null;
}
void clearSubjectContents(Subject toSubtract, Subject from) {
from.getPrincipals().removeAll(toSubtract.getPrincipals());
from.getPublicCredentials().removeAll(toSubtract.getPublicCredentials());
from.getPrivateCredentials().removeAll(toSubtract.getPrivateCredentials());
}
@Override
protected Principal getIdentity() {
Principal principal = new SimplePrincipal(userName);
return principal;
}
@Override
protected Group[] getRoleSets() throws LoginException {
return new Group[]{};
}
static Principal getPrincipal() {
if (System.getSecurityManager() == null) {
return SecurityContextAssociation.getPrincipal();
}
return AccessController.doPrivileged(new PrivilegedAction<Principal>() {
public Principal run() {
return SecurityContextAssociation.getPrincipal();
}
});
}
static Subject getSubject() {
if (System.getSecurityManager() == null) {
return SecurityContextAssociation.getSubject();
}
return AccessController.doPrivileged(new PrivilegedAction<Subject>() {
public Subject run() {
return SecurityContextAssociation.getSubject();
}
});
}
static Object makeCopy(final Subject from, final Subject to) {
if (System.getSecurityManager() == null) {
copy(from, to);
return null;
}
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
copy(from, to);
return null;
}
});
}
static void copy (final Subject from, final Subject to) {
for(Principal p:from.getPrincipals()) {
to.getPrincipals().add(p);
}
for (Object obj: from.getPrivateCredentials()) {
to.getPrivateCredentials().add(obj);
}
for (Object obj: from.getPublicCredentials()) {
to.getPublicCredentials().add(obj);
}
}
static void addPrivateCredential(final Subject subject, final Object obj) {
if (System.getSecurityManager() == null) {
subject.getPrivateCredentials().add(obj);
}
else {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
subject.getPrivateCredentials().add(obj);
return null;
}
});
}
}
static void removePrivateCredential(final Subject subject, final Object obj) {
if (System.getSecurityManager() == null) {
subject.getPrivateCredentials().remove(obj);
}
else {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
subject.getPrivateCredentials().remove(obj);
return null;
}
});
}
}
private static GSSCredential wrapCredential(final GSSCredential credential) {
return new GSSCredential() {
@Override
public int getUsage(Oid mech) throws GSSException {
return credential.getUsage(mech);
}
@Override
public int getUsage() throws GSSException {
return credential.getUsage();
}
@Override
public int getRemainingLifetime() throws GSSException {
return credential.getRemainingLifetime();
}
@Override
public int getRemainingInitLifetime(Oid mech) throws GSSException {
return credential.getRemainingInitLifetime(mech);
}
@Override
public int getRemainingAcceptLifetime(Oid mech) throws GSSException {
return credential.getRemainingAcceptLifetime(mech);
}
@Override
public GSSName getName(Oid mech) throws GSSException {
return credential.getName(mech);
}
@Override
public GSSName getName() throws GSSException {
return credential.getName();
}
@Override
public Oid[] getMechs() throws GSSException {
return credential.getMechs();
}
@Override
public void dispose() throws GSSException {
// Prevent disposal of our credential.
}
@Override
public void add(GSSName name, int initLifetime, int acceptLifetime, Oid mech, int usage) throws GSSException {
credential.add(name, initLifetime, acceptLifetime, mech, usage);
}
};
}
}