In this recipe you will see how to configure authentication cross forest and how you might implement cross forest authorization.

Cross Forest Tomcat Single Sign-on

Authentication is relatively simple to handle and requires only a couple of small changes to the configuration detailed in my previous post about Tomcat Single Sign-on with Kerberos.

You would simply add a new kdc section to the krb5.conf file for the trusted forest/realm as in the below example and it would probably be necessary to add an additional encryption type to both the default_tgt_enctypes list and the default_tgs_enctypes list for RC4-HMAC which is the Microsoft standard used for cross forest communication.

[libdefaults] 
default_realm=REALM_A 
default_keytab_name="CATALINA_HOME/conf/tomcat.keytab" 
default_tgt_enctypes=aes256-cts-hmac-shal-96,aes128-cts-hmac-shal-96, RC4-HMAC 
default_tgs_enctypes=aes256-cts-hmac-shal-96,aes128-cts-hmac-shal-96, RC4-HMAC 
forwardable=true

[realms] 
REALM_A={ 
    kdc=dc.mydomain.com:88 
} 
REALM_B={ 
    kdc=dc.mydomain.net:88 
}

[domain_realm] 
mydomain.com=REALM_A 
.mydomain.com=REALM_A 
mydomain.net=REALM_B 
.mydomain.net=REALM_B

The Tomcat JNDI Realm relies on the standard LDAP Directory context for looking up user information in the LDAP directory. It appears however that it is not possible to use Cross-forest LDAP Authorization in this way.

I have seen that in C# cross forest user authorization can be done via the Windows API for forests that have an established trust relationship, however in a pure Java environment (No JNA) the only means by which I have seen this done was by managing a list of “Active Directories” for each forest and caching all of the users for the forests in the trust. This is both a waste of resources, not very efficient and prone to error as updates will not be propagated. Outlined below are a problem, and a possible solution, for extending the JNDI realm in Tomcat.

Cross-forest LDAP Authorization

The following diagram shows an example of a complex configuration with Cross-forest authentication and authorization. In this particular configuration, there are also Kerberos realms or Windows domains (MYDOMAIN.COM, MYDOMAIN.NET), each with their own KDC and LDAP Forest. Users from MYDOMAIN.NET can be authenticated with Tomcat, as there is a trust relationship between the two KDCs or Domain Controllers. However, this configuration is not currently supported by Tomcat for Authorization because the JNDI realm doesn’t seem to follow referrals when the root forest is not common.

Figure 31: Load-Balanced Cluster Solution with Cross Realm Authentication Figure 31: Load-Balanced Cluster Solution with Cross Realm Authentication

Intended Operation:

A user of forest/realm A is a member of a group in forest/realm B, and should be able to get authorization for a service in domain B.

Problem Statement:

In Figure 22, a user, “Jane Doe” from the mydomain.info realm wants to log into Tomcat on the mydomain.com realm. Cross-domain authorization is therefore required. There is a forest trust between the two domains, and Jane Doe is a member of the “TomcatAdmin” group in the mydomain.com realm.

Figure 32: Overview of Cross-forest Authorization Figure 32: Overview of Cross-forest Authorization

When a user performs a Tomcat login, the Tomcat JNDI realm searches for that user in the mydomain.com LDAP forest, and then retrieves the user’s roles from its user properties. For users of another forest, the LDAP search for the user name fails, since users of trusted domains are not searchable by their distinguished name. Users and group memberships are defined by their SID (ForeignSecurityPrinciple: SID). This is highlighted in green on mydomain.com, where Jane Doe is represented by her SID S-1-5-21-87175589-2066007970-603339860-1113 as a member of the TomcatAdmin group.

Therefore, the search for “Jane Doe” on mydomain.com does not return any users.

Also, in Jane Doe’s user properties on the mydomain.net realm, the group membership from the other forest (mydomain.com) is not visible. This means that trying to authorize a user in their own domain would not lead to any results, as the search would return the user Jane Doe not being member of any group.

Approach #1:

If we were to retrieve the SID of the cross-forest user from their LDAP server and were to search mydomain.com using this SID, we could only check if the user is a member of the “TomcatAdmin” group. However, in case of nested groups, that is, if a user were not directly a member of the “TomcatAdmin” group but were in an intermediate group that is a member of “TomcatAdmin”, we would not be able to determine their access rights.

Approach #2:

By analyzing the SID structure, we can extract valuable information. At the bottom of Figure 6 is an example SID:

  • The first part is a fixed value (s-1-5).
  • The second part is the ID of the forest/domain (mydomain.net: 21-87175589-2066007970-603339860).
  • The third part is the binary SID of the user/group (Jane Doe: 1113).

Using the second part of the SID (domainId), we can look up the entry with objectClass=trustedDomain with the domainId and retrieve the correct domain name (mydomain.net) from the mydomain.com LDAP.

Knowing the domain of the SID, we can use the LDAP server for that forest to perform the authorization:

  • First, we verify using the GSSCredential of the authenticating user if they are a user from that forest.
  • If they are, we can check if the SID is their SID, or a group SID.
  • If the SID is theirs, we can assign their group rights.
  • In the event of a group SID, we can recourse the group-structured checking if our user is a member of any of the sub-groups in the tree.
  • If they are found, assign their rights.
  • Otherwise, reject them with a 403 Forbidden.

Here is a description of this algorithm using pseudo code:

if user is in current domain
    retrieve user and get roles from user properties
else
    for each Tomcat Group
        if a ForeignSecurityPrincipal is member of Tomcat group for the user domain 
            retrieve all elements from domain that match the SIDs from Tomcat
groups and check if our user is one of the members
            if user in list
                add group to users session
            else if user not one of the users 
                recurse groups, retrieve all users in groups and verify if user is member
                if user in one of these groups
                    add group to users session
                end if
            end if
        end if
    end loop
end if
if user has a group and that group has access to the requested area
    grant access
else
    deny access
end if

To enable handling of cross-forest authorization, the default behavior of the Tomcat JNDI realm needs to be extended using the algorithm described above.

In case of cross-domain authorization, we look for the Tomcat groups and verify a user’s membership instead of finding the user in LDAP to retrieve the groups.

Implementation details and queries:

  • To increase performance, group information can be cached, but should be updated based upon parameterized frequency.
  • Retrieval of all users in the group:
(&(cn=*)(|(memberOf:1.2.840.113556.1.4.1941:=cn=TomcatAdmin,cn=Users,dc=mydomain,dc=com)(memberOf:1.2.840.113556.1.4.1941:=cn=TomcatUser,cn=Users,dc=mydomain,dc=com)))
  • Retrieval of all trusted domain SIDs: (objectClass=trustedDomain)
  • Retrieval of all objects with the SIDs found:
(&(objectSid=<sid1>)(objectSid=<sid2>)(objectSid=<sid3>))
  • Retrieval for each all foreign groups in our group:
(&(cn=*)(|(memberOf:1.2.840.113556.1.4.1941:=<baseDNforSIDGroup1>)(memberOf:1.2.840.113556.1.4.1941:= <baseDNforSIDGroup2>)))

Note: You should be aware that the DNS domain of the Tomcat machines must match that of the Windows domain. For example, if your domain is mydomain.com, you cannot have an Tomcat server called www.myhost.com because the KDC in the second realm will not know where to find the SPN for the appropriate given host.