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
+     * &quot;@...&quot; 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&apos;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&apos;s delegated credentials and exposing the user&apos;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 &quot;@...&quot; 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 &quot;@...&quot; 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 &quot;@...&quot; 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 &quot;@...&quot; 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 &quot;@...&quot; 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&apos;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

Reply via email to