From 905974397811034031e3cc4589c4e054558f10d9 Mon Sep 17 00:00:00 2001 From: markt Date: Thu, 31 Mar 2011 19:33:04 +0000 Subject: [PATCH] SPNEGO support part 2 Expose the users delegated credentials through a request attribute so applications can make use of it git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1087416 13f79535-47bb-0310-9956-ffa450edef68 --- java/org/apache/catalina/Globals.java | 4 +++ java/org/apache/catalina/Realm.java | 6 ++-- .../authenticator/SpnegoAuthenticator.java | 12 +++++++- java/org/apache/catalina/connector/Request.java | 7 +++++ java/org/apache/catalina/realm/CombinedRealm.java | 4 +-- .../apache/catalina/realm/GenericPrincipal.java | 36 ++++++++++++++++++++++ .../apache/catalina/realm/LocalStrings.properties | 1 + java/org/apache/catalina/realm/LockOutRealm.java | 5 +-- java/org/apache/catalina/realm/RealmBase.java | 31 +++++++++++++++++-- .../apache/catalina/session/StandardSession.java | 3 +- webapps/docs/config/valve.xml | 9 ++++++ 11 files changed, 107 insertions(+), 11 deletions(-) diff --git a/java/org/apache/catalina/Globals.java b/java/org/apache/catalina/Globals.java index 1505a2b8f..124378ce2 100644 --- a/java/org/apache/catalina/Globals.java +++ b/java/org/apache/catalina/Globals.java @@ -151,6 +151,10 @@ public final class Globals { "javax.security.auth.subject"; + public static final String GSS_CREDENTIAL_ATTR = + "org.apache.catalina.realm.GSS_CREDENTIAL"; + + /** * The master flag which controls strict servlet specification * compliance. diff --git a/java/org/apache/catalina/Realm.java b/java/org/apache/catalina/Realm.java index b9e85c138..f03f4b6dd 100644 --- a/java/org/apache/catalina/Realm.java +++ b/java/org/apache/catalina/Realm.java @@ -111,9 +111,11 @@ public interface Realm { * Return the Principal associated with the specified chain of X509 * client certificates. If there is none, return null. * - * @param certs The gssContext processed by the {@link Authenticator}. + * @param gssContext The gssContext processed by the {@link Authenticator}. + * @param storeCreds Should the realm attempt to store the delegated + * credentials in the returned Principal? */ - public Principal authenticate(GSSContext gssContext); + public Principal authenticate(GSSContext gssContext, boolean storeCreds); /** diff --git a/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java b/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java index b30cfd322..8dc05faac 100644 --- a/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java +++ b/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java @@ -77,6 +77,15 @@ public class SpnegoAuthenticator extends AuthenticatorBase { this.loginConfigName = loginConfigName; } + private boolean storeDelegatedCredentials = true; + public boolean isStoreDelegatedCredentials() { + return storeDelegatedCredentials; + } + public void setStoreDelegatedCredentials( + boolean storeDelegatedCredentials) { + this.storeDelegatedCredentials = storeDelegatedCredentials; + } + @Override protected String getAuthMethod() { @@ -229,7 +238,8 @@ public class SpnegoAuthenticator extends AuthenticatorBase { return false; } - principal = context.getRealm().authenticate(gssContext); + principal = context.getRealm().authenticate(gssContext, + storeDelegatedCredentials); } catch (GSSException e) { if (log.isDebugEnabled()) { log.debug(sm.getString("spnegoAuthenticator.ticketValidateFail", diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java index e90055be2..ff6283fa5 100644 --- a/java/org/apache/catalina/connector/Request.java +++ b/java/org/apache/catalina/connector/Request.java @@ -923,6 +923,13 @@ public class Request return asyncSupported; } + if (name.equals(Globals.GSS_CREDENTIAL_ATTR)) { + if (userPrincipal instanceof GenericPrincipal) { + return ((GenericPrincipal) userPrincipal).getGssCredential(); + } + return null; + } + Object attr=attributes.get(name); if(attr!=null) diff --git a/java/org/apache/catalina/realm/CombinedRealm.java b/java/org/apache/catalina/realm/CombinedRealm.java index b6cd91e86..70b2e7f1a 100644 --- a/java/org/apache/catalina/realm/CombinedRealm.java +++ b/java/org/apache/catalina/realm/CombinedRealm.java @@ -271,7 +271,7 @@ public class CombinedRealm extends RealmBase { * {@inheritDoc} */ @Override - public Principal authenticate(GSSContext gssContext) { + public Principal authenticate(GSSContext gssContext, boolean storeCreds) { if (gssContext.isEstablished()) { Principal authenticatedUser = null; String username = null; @@ -292,7 +292,7 @@ public class CombinedRealm extends RealmBase { username, realm.getInfo())); } - authenticatedUser = realm.authenticate(gssContext); + authenticatedUser = realm.authenticate(gssContext, storeCreds); if (authenticatedUser == null) { if (log.isDebugEnabled()) { diff --git a/java/org/apache/catalina/realm/GenericPrincipal.java b/java/org/apache/catalina/realm/GenericPrincipal.java index 22f753624..6b45faf26 100644 --- a/java/org/apache/catalina/realm/GenericPrincipal.java +++ b/java/org/apache/catalina/realm/GenericPrincipal.java @@ -25,6 +25,8 @@ import java.util.List; import javax.security.auth.login.LoginContext; +import org.ietf.jgss.GSSCredential; + /** * Generic implementation of java.security.Principal that @@ -98,6 +100,26 @@ public class GenericPrincipal implements Principal { */ public GenericPrincipal(String name, String password, List roles, Principal userPrincipal, LoginContext loginContext) { + this(name, password, roles, userPrincipal, loginContext, null); + } + + /** + * Construct a new Principal, associated with the specified Realm, for the + * specified username and password, with the specified role names + * (as Strings). + * + * @param name The username of the user represented by this Principal + * @param password Credentials used to authenticate this user + * @param roles List of roles (must be Strings) possessed by this user + * @param userPrincipal - the principal to be returned from the request + * getUserPrincipal call if not null; if null, this will be returned + * @param loginContext - If provided, this will be used to log out the user + * at the appropriate time + * @param gssCredential - If provided, the user's delegated credentials + */ + public GenericPrincipal(String name, String password, List roles, + Principal userPrincipal, LoginContext loginContext, + GSSCredential gssCredential) { super(); this.name = name; this.password = password; @@ -109,6 +131,7 @@ public class GenericPrincipal implements Principal { Arrays.sort(this.roles); } this.loginContext = loginContext; + this.gssCredential = gssCredential; } @@ -167,6 +190,19 @@ public class GenericPrincipal implements Principal { */ protected LoginContext loginContext = null; + + /** + * The user's delegated credentials. + */ + protected GSSCredential gssCredential = null; + + public GSSCredential getGssCredential() { + return this.gssCredential; + } + protected void setGssCredential(GSSCredential gssCredential) { + this.gssCredential = gssCredential; + } + // --------------------------------------------------------- Public Methods diff --git a/java/org/apache/catalina/realm/LocalStrings.properties b/java/org/apache/catalina/realm/LocalStrings.properties index 01fb5a8ef..c2485019d 100644 --- a/java/org/apache/catalina/realm/LocalStrings.properties +++ b/java/org/apache/catalina/realm/LocalStrings.properties @@ -65,6 +65,7 @@ memoryRealm.readXml=Exception while reading memory database file memoryRealm.xmlFeatureEncoding=Exception configuring digester to permit java encoding names in XML files. Only IANA encoding names will be supported. realmBase.algorithm=Invalid message digest algorithm {0} specified realmBase.alreadyStarted=This Realm has already been started +realmBase.delegatedCredentialFail=Unable to obtain delegated credentials for user [{0} realmBase.digest=Error digesting user credentials realmBase.forbidden=Access to the requested resource has been denied realmBase.hasRoleFailure=Username {0} does NOT have role {1} diff --git a/java/org/apache/catalina/realm/LockOutRealm.java b/java/org/apache/catalina/realm/LockOutRealm.java index 7059a85eb..194e42cef 100644 --- a/java/org/apache/catalina/realm/LockOutRealm.java +++ b/java/org/apache/catalina/realm/LockOutRealm.java @@ -225,7 +225,7 @@ public class LockOutRealm extends CombinedRealm { * {@inheritDoc} */ @Override - public Principal authenticate(GSSContext gssContext) { + public Principal authenticate(GSSContext gssContext, boolean storeCreds) { if (gssContext.isEstablished()) { String username = null; GSSName name = null; @@ -246,7 +246,8 @@ public class LockOutRealm extends CombinedRealm { return null; } - Principal authenticatedUser = super.authenticate(gssContext); + Principal authenticatedUser = + super.authenticate(gssContext, storeCreds); if (authenticatedUser == null) { registerAuthFailure(username); diff --git a/java/org/apache/catalina/realm/RealmBase.java b/java/org/apache/catalina/realm/RealmBase.java index fc5ad22fc..323ba835b 100644 --- a/java/org/apache/catalina/realm/RealmBase.java +++ b/java/org/apache/catalina/realm/RealmBase.java @@ -55,6 +55,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.res.StringManager; import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSName; @@ -424,7 +425,7 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { * {@inheritDoc} */ @Override - public Principal authenticate(GSSContext gssContext) { + public Principal authenticate(GSSContext gssContext, boolean storeCred) { if (gssContext.isEstablished()) { GSSName name = null; try { @@ -434,7 +435,20 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { } if (name!= null) { - return getPrincipal(name.toString()); + GSSCredential gssCredential = null; + if (storeCred && gssContext.getCredDelegState()) { + try { + gssCredential = gssContext.getDelegCred(); + } catch (GSSException e) { + e.printStackTrace(); + if (log.isDebugEnabled()) { + log.debug(sm.getString( + "realmBase.delegatedCredentialFail", name), + e); + } + } + } + return getPrincipal(name.toString(), gssCredential); } } @@ -785,7 +799,7 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { if (roles.length == 0 && !constraint.getAllRoles()) { if(constraint.getAuthConstraint()) { if( log.isDebugEnabled() ) - log.debug("No roles "); + log.debug("No role)s "); status = false; // No listed roles means no access at all denyfromall = true; break; @@ -1181,6 +1195,17 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { protected abstract Principal getPrincipal(String username); + protected Principal getPrincipal(String username, + GSSCredential gssCredential) { + Principal p = getPrincipal(username); + + if (p instanceof GenericPrincipal) { + ((GenericPrincipal) p).setGssCredential(gssCredential); + } + + return p; + } + /** * Return the Server object that is the ultimate parent for the container * with which this Realm is associated. If the server cannot be found (eg diff --git a/java/org/apache/catalina/session/StandardSession.java b/java/org/apache/catalina/session/StandardSession.java index a418ce57e..626013ff4 100644 --- a/java/org/apache/catalina/session/StandardSession.java +++ b/java/org/apache/catalina/session/StandardSession.java @@ -176,7 +176,8 @@ public class StandardSession implements HttpSession, Session, Serializable { * Set of attribute names which are not allowed to be persisted. */ protected static final String[] excludedAttributes = { - Globals.SUBJECT_ATTR + Globals.SUBJECT_ATTR, + Globals.GSS_CREDENTIAL_ATTR }; diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml index b3a77aa3c..8cd90ab8d 100644 --- a/webapps/docs/config/valve.xml +++ b/webapps/docs/config/valve.xml @@ -891,6 +891,15 @@ specified, the platform default provider will be used.

+ +

Controls if the user' delegated credentials will be stored in + the user Principal. If available, the delegated credentials will be + available to applications (e.g. for onward authentication to external + services) via the org.apache.catalina.realm.GSS_CREDENTIAL + request attribute.If not set, the default value of true + will be used.

+
+ -- 2.11.0