From: markt Date: Fri, 1 Apr 2011 00:34:45 +0000 (+0000) Subject: SPNEGP part 3 - the final part for 7.0.12 X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=ad3be5f67a1dc36d553a80e33ce81822ce79f0ad;p=tomcat7.0 SPNEGP part 3 - the final part for 7.0.12 Integrate with JNDI realm so delegated credentials are used by default. git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1087524 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java b/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java index 8dc05faac..e862864ba 100644 --- a/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java +++ b/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java @@ -46,24 +46,6 @@ import org.ietf.jgss.Oid; * multiple components. If the configuration is invalid, the error messages are * often cryptic although a Google search will usually point you in the right * direction. - *

- * TODO: - *

- *

- * TBDs: - *

*/ public class SpnegoAuthenticator extends AuthenticatorBase { diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java index 430ad4811..a65802af4 100644 --- a/java/org/apache/catalina/realm/JNDIRealm.java +++ b/java/org/apache/catalina/realm/JNDIRealm.java @@ -56,6 +56,7 @@ import org.apache.catalina.LifecycleException; import org.apache.catalina.util.Base64; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.CharChunk; +import org.ietf.jgss.GSSCredential; /** *

Implementation of Realm that works with a directory @@ -415,6 +416,14 @@ public class JNDIRealm extends RealmBase { */ protected int timeLimit = 0; + + /** + * Should delegated credentials from the SPNEGO authenticator be used if + * available + */ + protected boolean useDelegatedCredential = true; + + // ------------------------------------------------------------- Properties /** @@ -950,6 +959,15 @@ public class JNDIRealm extends RealmBase { } + + public boolean isUseDelegatedCredential() { + return useDelegatedCredential; + } + + public void setUseDelegatedCredential(boolean useDelegatedCredential) { + this.useDelegatedCredential = useDelegatedCredential; + } + /** * Return descriptive information about this Realm implementation and * the corresponding version number, in the format @@ -1935,6 +1953,12 @@ public class JNDIRealm extends RealmBase { */ @Override protected Principal getPrincipal(String username) { + return getPrincipal(username, null); + } + + @Override + protected Principal getPrincipal(String username, + GSSCredential gssCredential) { DirContext context = null; Principal principal = null; @@ -1949,7 +1973,7 @@ public class JNDIRealm extends RealmBase { try { // Authenticate the specified username if possible - principal = getPrincipal(context, username); + principal = getPrincipal(context, username, gssCredential); } catch (CommunicationException e) { @@ -1964,7 +1988,7 @@ public class JNDIRealm extends RealmBase { context = open(); // Try the authentication again. - principal = getPrincipal(context, username); + principal = getPrincipal(context, username, gssCredential); } catch (ServiceUnavailableException e) { @@ -1979,7 +2003,7 @@ public class JNDIRealm extends RealmBase { context = open(); // Try the authentication again. - principal = getPrincipal(context, username); + principal = getPrincipal(context, username, gssCredential); } @@ -2012,14 +2036,52 @@ public class JNDIRealm extends RealmBase { * Return the Principal associated with the given user name. */ protected synchronized Principal getPrincipal(DirContext context, - String username) + String username, GSSCredential gssCredential) throws NamingException { - User user = getUser(context, username); + User user = null; + List roles = null; + + try { + if (gssCredential != null && isUseDelegatedCredential()) { + // Set up context + context.addToEnvironment( + Context.SECURITY_AUTHENTICATION, "GSSAPI"); + context.addToEnvironment( + "javax.security.sasl.server.authentication", "true"); + context.addToEnvironment( + "javax.security.sasl.qop", "auth-conf"); + // Note: Subject already set in SPNEGO authenticator so no need + // for Subject.doAs() here + } + user = getUser(context, username); + if (user != null) { + roles = getRoles(context, user); + } + } finally { + try { + context.removeFromEnvironment( + Context.SECURITY_AUTHENTICATION); + } catch (NamingException e) { + // Ignore + } + try { + context.removeFromEnvironment( + "javax.security.sasl.server.authentication"); + } catch (NamingException e) { + // Ignore + } + try { + context.removeFromEnvironment( + "javax.security.sasl.qop"); + } catch (NamingException e) { + // Ignore + } + } if (user != null) { return new GenericPrincipal(user.getUserName(), user.getPassword(), - getRoles(context, user)); + roles, null, null, gssCredential); } return null; @@ -2303,45 +2365,45 @@ public class JNDIRealm extends RealmBase { } - // ------------------------------------------------------ Private Classes - - /** - * A protected class representing a User - */ - protected static class User { + // ------------------------------------------------------ Private Classes + + /** + * A protected class representing a User + */ + protected static class User { - private final String username; - private final String dn; - private final String password; - private final List roles; - - public User(String username, String dn, String password, - List roles) { - this.username = username; - this.dn = dn; - this.password = password; - if (roles == null) { - this.roles = Collections.emptyList(); - } else { - this.roles = Collections.unmodifiableList(roles); - } - } + private final String username; + private final String dn; + private final String password; + private final List roles; + + public User(String username, String dn, String password, + List roles) { + this.username = username; + this.dn = dn; + this.password = password; + if (roles == null) { + this.roles = Collections.emptyList(); + } else { + this.roles = Collections.unmodifiableList(roles); + } + } - public String getUserName() { - return username; - } - - public String getDN() { - return dn; - } + public String getUserName() { + return username; + } - public String getPassword() { - return password; - } + public String getDN() { + return dn; + } + + public String getPassword() { + return password; + } - public List getRoles() { - return roles; - } - } + public List getRoles() { + return roles; + } + } } diff --git a/java/org/apache/catalina/realm/RealmBase.java b/java/org/apache/catalina/realm/RealmBase.java index 323ba835b..d7a557ac9 100644 --- a/java/org/apache/catalina/realm/RealmBase.java +++ b/java/org/apache/catalina/realm/RealmBase.java @@ -151,6 +151,13 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { protected AllRolesMode allRolesMode = AllRolesMode.STRICT_MODE; + /** + * When processing users authenticated via the GSS-API, should any + * "@..." be stripped from the end of the user name? + */ + protected boolean stripAtForGss = true; + + // ------------------------------------------------------------- Properties @@ -272,6 +279,16 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { } + public boolean isStripAtForGss() { + return stripAtForGss; + } + + + public void setStripAtForGss(boolean stripAtForGss) { + this.stripAtForGss = stripAtForGss; + } + + // --------------------------------------------------------- Public Methods @@ -427,14 +444,23 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { @Override public Principal authenticate(GSSContext gssContext, boolean storeCred) { if (gssContext.isEstablished()) { - GSSName name = null; + GSSName gssName = null; try { - name = gssContext.getSrcName(); + gssName = gssContext.getSrcName(); } catch (GSSException e) { log.warn(sm.getString("realmBase.gssNameFail"), e); } - if (name!= null) { + if (gssName!= null) { + String name = gssName.toString(); + + if (isStripAtForGss()) { + int i = name.indexOf('@'); + if (i > 0) { + // Zero so we don;t leave a zero length name + name = name.substring(0, i); + } + } GSSCredential gssCredential = null; if (storeCred && gssContext.getCredDelegState()) { try { @@ -448,7 +474,7 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { } } } - return getPrincipal(name.toString(), gssCredential); + return getPrincipal(name, gssCredential); } } diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 8e35a7593..f4800ae34 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -95,9 +95,13 @@ Don't register Contexts that fail to start with the Mapper. (markt) - Add initial support for SPNEGO/Kerberos authentication also referred to - as integrated Windows authentication. This is a work in progress. See - the documentation for details. (markt) + 48685: Add initial support for SPNEGO/Kerberos authentication + also referred to as integrated Windows authentication. This includes + user authentication, authorisation via the directory using the + user's delegated credentials and exposing the user's delegated + credentials via a request attribute so applications can make use of the + to impersonate the current user when accessing third-party systems that + use a compatible authentication mechanism. (markt) HTTP range requests cannot be reliably served when a Writer is in use so diff --git a/webapps/docs/config/realm.xml b/webapps/docs/config/realm.xml index 3c7f766e5..d2f533a3d 100644 --- a/webapps/docs/config/realm.xml +++ b/webapps/docs/config/realm.xml @@ -143,6 +143,12 @@ a role name assigned to the corresponding user.

+ +

When processing users authenticated via the GSS-API, this attribute + controls if any "@..." is removed from the end of the user + name. If not specified, the default is true.

+
+

Name of the column, in the "users" table, which contains the user's credentials (i.e. password(. If a value for the @@ -224,6 +230,12 @@ a role name assigned to the corresponding user.

+ +

When processing users authenticated via the GSS-API, this attribute + controls if any "@..." is removed from the end of the user + name. If not specified, the default is true.

+
+

Name of the column, in the "users" table, which contains the user's credentials (i.e. password(. If a value for the @@ -420,7 +432,9 @@ user currently being authenticated? If false, connectionName} and connectionPassword will be used if specified, else an anonymous. If not specified, the default - value of false is used.

+ value of false is used. Note that when accessing the + directory using delegated credentials, this attribute is always ignored + and the search is performed using the delegated credentials.

@@ -437,6 +451,12 @@ 0 is used which indicates no limit.

+ +

When processing users authenticated via the GSS-API, this attribute + controls if any "@..." is removed from the end of the user + name. If not specified, the default is true.

+
+

Specifies the time (in milliseconds) to wait for records to be returned when using the userSearch attribute. If not @@ -444,6 +464,14 @@ limit.

+ +

When the JNIRealm is used with the SPNEGO authenticator, delegated + credentials for the user may be available. If such credentials are + present, this attribute controls whether are not they are used to + connect to the directory. If not specified, the default value of + true is used.

+
+

The base element for user searches performed using the userSearch expression. Not used if you are using @@ -471,7 +499,11 @@ actual username should be inserted. You can use this property instead of userSearch, userSubtree and userBase when the distinguished name contains - the username and is otherwise the same for all users.

+ the username and is otherwise the same for all users. Note that + when accessing the directory using delegated credentials, this + attribute is always ignored and userSearch, + userSubtree and userBase are always + used instead.

@@ -565,6 +597,12 @@ default value is conf/tomcat-users.xml.

+ +

When processing users authenticated via the GSS-API, this attribute + controls if any "@..." is removed from the end of the user + name. If not specified, the default is true.

+
+

The XML document referenced by the pathname attribute must @@ -634,6 +672,12 @@ for your role Principals.

+ +

When processing users authenticated via the GSS-API, this attribute + controls if any "@..." is removed from the end of the user + name. If not specified, the default is true.

+
+

Instructs JAASRealm to use the context class loader for loading the user-specified LoginModule class and associated diff --git a/webapps/docs/windows-auth-howto.xml b/webapps/docs/windows-auth-howto.xml index a954f5279..eff4771a3 100644 --- a/webapps/docs/windows-auth-howto.xml +++ b/webapps/docs/windows-auth-howto.xml @@ -51,10 +51,18 @@ sections.

-

This is a work in progress. This warning should be removed once the -various questions and TODOs (see the Javadoc and implementation class) have been -resolved. In particular, onwards delegation is not yet supported and -roles are not retrieved from the domain controller.

+

This is a work in progress. There are a number of outstanding +questions that require further testing. These include: +

    +
  • Does the domain name have to be in upper case?
  • +
  • Does the SPN have to start with HTTP/...?
  • +
  • Can a port number be appended to the end of the host in the SPN?
  • +
  • Can the domain be left off the user in the ktpass command?
  • +
  • What are the limitations on the account that Tomcat can run as? SPN + associated account works, domain admin works, local admin doesn't + work
  • +
+

There are four components to the configuration of the built-in Tomcat support for Windows authentication. The domain controller, the server hosting Tomcat, the web application wishing to use Windows authentication and the client @@ -156,6 +164,10 @@ com.sun.security.jgss.krb5.accept { is automatically set to the required value of false if a web application is configured to use the SPNEGO authentication method.

+

The SPNEGO authenticator will work with any + Realm but if used with the JNDI Realm, by default the JNDI Realm will use + the user's delegated credentials to connect to the Active Directory. +

The above steps have been tested on a Tomcat server running Windows Server 2008 R2 64-bit Standard with an Oracle 1.6.0_24 64-bit JDK.