Author: markt Date: Fri Apr 1 00:34:45 2011 New Revision: 1087524 URL: http://svn.apache.org/viewvc?rev=1087524&view=rev Log: SPNEGP part 3 - the final part for 7.0.12 Integrate with JNDI realm so delegated credentials are used by default.
Modified: tomcat/trunk/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java tomcat/trunk/java/org/apache/catalina/realm/JNDIRealm.java tomcat/trunk/java/org/apache/catalina/realm/RealmBase.java tomcat/trunk/webapps/docs/changelog.xml tomcat/trunk/webapps/docs/config/realm.xml tomcat/trunk/webapps/docs/windows-auth-howto.xml Modified: tomcat/trunk/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java?rev=1087524&r1=1087523&r2=1087524&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java (original) +++ tomcat/trunk/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java Fri Apr 1 00:34:45 2011 @@ -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. - * <p> - * TODO: - * <ul> - * <li>Add support for delegating credentials? Need this if we want to - * authenticate to a realm as the user. This is likely to result in a fair - * amount of internal refactoring.</li> - * </ul> - * <p> - * TBDs: - * <ul> - * <li>Does the domain name have to be in upper case?</li> - * <li>Does the SPN have to start with HTTP/...?</li> - * <li>Can a port number be appended to the end of the host in the SPN?</li> - * <li>Can the domain be left off the user in the ktpass command?</li> - * <li>What are the limitations on the account that Tomcat can run as? SPN - * associated account works, domain admin works, local admin doesn't - * work</li> - * </ul> */ public class SpnegoAuthenticator extends AuthenticatorBase { Modified: tomcat/trunk/java/org/apache/catalina/realm/JNDIRealm.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/realm/JNDIRealm.java?rev=1087524&r1=1087523&r2=1087524&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/realm/JNDIRealm.java (original) +++ tomcat/trunk/java/org/apache/catalina/realm/JNDIRealm.java Fri Apr 1 00:34:45 2011 @@ -56,6 +56,7 @@ import org.apache.catalina.LifecycleExce 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; /** * <p>Implementation of <strong>Realm</strong> 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<String> 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<String> roles; - - public User(String username, String dn, String password, - List<String> 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<String> roles; + + public User(String username, String dn, String password, + List<String> 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<String> getRoles() { - return roles; - } - } + public List<String> getRoles() { + return roles; + } + } } Modified: tomcat/trunk/java/org/apache/catalina/realm/RealmBase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/realm/RealmBase.java?rev=1087524&r1=1087523&r2=1087524&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/realm/RealmBase.java (original) +++ tomcat/trunk/java/org/apache/catalina/realm/RealmBase.java Fri Apr 1 00:34:45 2011 @@ -151,6 +151,13 @@ public abstract class RealmBase extends 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 } + public boolean isStripAtForGss() { + return stripAtForGss; + } + + + public void setStripAtForGss(boolean stripAtForGss) { + this.stripAtForGss = stripAtForGss; + } + + // --------------------------------------------------------- Public Methods @@ -427,14 +444,23 @@ public abstract class RealmBase extends @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 } } } - return getPrincipal(name.toString(), gssCredential); + return getPrincipal(name, gssCredential); } } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1087524&r1=1087523&r2=1087524&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Fri Apr 1 00:34:45 2011 @@ -95,9 +95,13 @@ Don't register Contexts that fail to start with the Mapper. (markt) </fix> <add> - 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) + <bug>48685</bug>: 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) </add> <fix> HTTP range requests cannot be reliably served when a Writer is in use so Modified: tomcat/trunk/webapps/docs/config/realm.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/realm.xml?rev=1087524&r1=1087523&r2=1087524&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/realm.xml (original) +++ tomcat/trunk/webapps/docs/config/realm.xml Fri Apr 1 00:34:45 2011 @@ -143,6 +143,12 @@ a role name assigned to the corresponding user.</p> </attribute> + <attribute name="stripAtForGss" required="false"> + <p>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 <code>true</code>.</p> + </attribute> + <attribute name="userCredCol" required="true"> <p>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.</p> </attribute> + <attribute name="stripAtForGss" required="false"> + <p>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 <code>true</code>.</p> + </attribute> + <attribute name="userCredCol" required="true"> <p>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, <code>connectionName</code>} and <code>connectionPassword</code> will be used if specified, else an anonymous. If not specified, the default - value of <code>false</code> is used.</p> + value of <code>false</code> 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.</p> </attribute> <attribute name="roleSubtree" required="false"> @@ -437,6 +451,12 @@ <code>0</code> is used which indicates no limit.</p> </attribute> + <attribute name="stripAtForGss" required="false"> + <p>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 <code>true</code>.</p> + </attribute> + <attribute name="timeLimit" required="false"> <p>Specifies the time (in milliseconds) to wait for records to be returned when using the <code>userSearch</code> attribute. If not @@ -444,6 +464,14 @@ limit.</p> </attribute> + <attribute name="useDelegatedCredential" required="false"> + <p>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 + <code>true</code> is used.</p> + </attribute> + <attribute name="userBase" required="false"> <p>The base element for user searches performed using the <code>userSearch</code> expression. Not used if you are using @@ -471,7 +499,11 @@ actual username should be inserted. You can use this property instead of <code>userSearch</code>, <code>userSubtree</code> and <code>userBase</code> when the distinguished name contains - the username and is otherwise the same for all users.</p> + 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 <code>userSearch</code>, + <code>userSubtree</code> and <code>userBase</code> are always + used instead.</p> </attribute> <attribute name="userRoleName" required="false"> @@ -565,6 +597,12 @@ default value is <code>conf/tomcat-users.xml</code>.</p> </attribute> + <attribute name="stripAtForGss" required="false"> + <p>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 <code>true</code>.</p> + </attribute> + </attributes> <p>The XML document referenced by the <code>pathname</code> attribute must @@ -634,6 +672,12 @@ for your role <code>Principals</code>.</p> </attribute> + <attribute name="stripAtForGss" required="false"> + <p>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 <code>true</code>.</p> + </attribute> + <attribute name="useContextClassLoader" required="false"> <p>Instructs JAASRealm to use the context class loader for loading the user-specified <code>LoginModule</code> class and associated Modified: tomcat/trunk/webapps/docs/windows-auth-howto.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/windows-auth-howto.xml?rev=1087524&r1=1087523&r2=1087524&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/windows-auth-howto.xml (original) +++ tomcat/trunk/webapps/docs/windows-auth-howto.xml Fri Apr 1 00:34:45 2011 @@ -51,10 +51,18 @@ sections.</p> </section> <section name="Built-in Tomcat support"> -<p><strong>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.</strong> In particular, onwards delegation is not yet supported and -roles are not retrieved from the domain controller.</p> +<p><strong>This is a work in progress. There are a number of outstanding +questions that require further testing.</strong> These include: +<ul> +<li>Does the domain name have to be in upper case?</li> +<li>Does the SPN have to start with HTTP/...?</li> +<li>Can a port number be appended to the end of the host in the SPN?</li> +<li>Can the domain be left off the user in the ktpass command?</li> +<li>What are the limitations on the account that Tomcat can run as? SPN + associated account works, domain admin works, local admin doesn't + work</li> +</ul> +</p> <p>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.</li> </p> + <p>The SPNEGO authenticator will work with any <a href="config/realm.html"> + Realm</a> 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. + </p> <p>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.</p> </subsection> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org