Applied Crypto
Hassle free single sign-on integrated with your enterprise windows domain Online Users: 5
(c) copyright
appliedcrypto.com

  Home Products Support  
Articles
References
SPNEGO SSO

PAC (Privilege Access Certificate) in a Java Web Server World

PAC (Privilege Attribute Certificate) in a Java Web Server World





June, 2005

Author:
Jens Bo Friis
Partner, IT Architect, IT Practice A/S
© 2005 IT Practice A/S

Keywords: Kerberos, PAC, SPNEGO, Tomcat, WebLogic, WebSphere, J2EE, Security

Introduction

This paper describes how the Microsoft Kerberos Authentication Group Membership Extension: Privilege Attribute Certificate (PAC) can be used in a Java world where Kerberos is used to authenticate a user on a server using the Kerberos authentication.

This paper will describe some of the advantages and disadvantages of using the PAC for authorization on the server.

Various scenario and web server integration will be discussed.

The basic setup

In an Active Directory environment which using Kerberos as authentication mechanism, Microsoft has added an extension they call PAC. The PAC is data which contains the groups that the authenticated user is member of. The PAC is sealed, encrypted by the target server’s encryption key. Only the server can decrypt the PAC.

In fact, the PAC is just data put into the authorization-data field in the Kerberos authentication ticket [1].

The encrypted ticket part, which the authorization-data is part of, is encrypted with the target server (service)’s encryption key.

According to RCF 1510 specification this authorization-data field can be used for target server specific information. Microsoft is using this field to send the user-group membership to the target server.

The PAC

The PAC consists of several fields; a c-type struct which besides the group membership also contains information about the user account [2]: various user account time-stamps, logon count, bad password count and more.

The PAC is NDR encoded [3]. This encoding format describes how to locate specific fields inside the decrypted PAC byte array.

class PAC {
 uint64 LogonTime;
 uint64 LogoffTime;
 uint64 KickOffTime;
 uint64 PasswdLastSet;
 uint64 PasswdCanChange;
 uint64 PasswdMustChange;
 unicodehdr effectivename;
 unicodehdr fullname;
 unicodehdr logonscript;
 unicodehdr profilepath;
 unicodehdr homedirectory;
 unicodehdr homedirectorydrive;
 ushort logonCount;
 ushort badPasswdCount;
 ulong userid;
 ulong primarygroup;
 ulong groupcount;
 GROUP_MEMBERSHIP *groupids; // if groupcount>0
 long userflags;
 USER_SESSION_KEY key;
 unicodestring logonserver;
 unicodestring domainname;
 SID *logondomainid;
 long expansionroom[10];
 long sidcount;
 SID *sidsAndAttributes; // if sidcount>0
 ulong resourceGroupCount;
 GROUP_MEMBERSHIP *resourceGroupIDS; // if resourceGroupCount>0
} VALIDATION_SAM_INFO2;

Validating that you have got the decoding right can easily be done by looking up the specified user account using an LDAP browser.

The interesting part is the GROUP_MEMBERSHIP attribute. This is a pointer which points to a relative location starting just after the structure in the decrypted PAC byte array.

The GROUP_MEMBERSHIP* is an NDR array starting with a length field following with a NDR (long) relative group ID and a NDR (long) group attribute.

0140: 00 00 00 00 00 00 00 00 - 00 00 00 00 05 00 00 00 ................
0150: 5D 04 00 00 07 00 00 00 - 61 04 00 00 07 00 00 00 ].......a.......
0160: 7C 04 00 00 07 00 00 00 - 7D 04 00 00 07 00 00 00 |.......}.......
0170: 01 02 00 00 07 00 00 00 - 07 00 00 00 00 00 00 00 ................

Above, highlighted in yellow, is where the group ID array in the PAC is located. From the above example, we see that the array has a length of 5, which means that the specified authenticated user is member of 5 groups.

The groups are: 045D, 0461, 047C, 047D, 0201. Note that they are in little-endian encoding format.

Now, this doesn’t really look like groups. But they are. They are what Microsoft call relative group id’s. They are unique across directory tree’s and forests and is managed by the master DC [6].

If you look at a group defined in Active Directory you could see something like:

Expanding base 'CN=spnegousers,CN=Users,DC=TEST,DC=NET'...
Result <0>: (null)
Matched DNs: 
Getting 1 entries:
>> Dn: CN=spnegousers,CN=Users,DC=TEST,DC=NET
 4> member: CN=userxx,OU=ou11,OU=ou1,DC=TEST,DC=NET; CN=test5,CN=Users,DC=TEST,DC=NET; CN=Bo Friis,CN=Users,DC=TEST,DC=NET; CN=test,CN=Users,DC=TEST,DC=NET; 
 1> cn: spnegousers; 
 1> groupType: -2147483646; 
 1> instanceType: 4; 
 1> distinguishedName: CN=spnegousers,CN=Users,DC=TEST,DC=NET; 
 1> objectCategory: CN=Group,CN=Schema,CN=Configuration,DC=TEST,DC=NET; 
 2> objectClass: top; group; 
 1> objectGUID: 712c3320-dc87-472b-9d80-4d2d16637675; 
 1> objectSid: S-15-72FBE2E6-814C1995-C64F68B9-45D; 
 1> name: spnegousers; 
 1> sAMAccountName: spnegousers; 
 1> sAMAccountType: 268435456; 
 1> uSNChanged: 24518; 
 1> uSNCreated: 5704; 
 1> whenChanged: 2/11/2005 1:51:55 Romance Standard Time Romance Daylight Time; 
 1> whenCreated: 6/23/2004 22:25:16 Romance Standard Time Romance Daylight Time;

Looking at the attribute Object SID: objectSid: S-15-72FBE2E6-814C1995-C64F68B9-45D; you will notice the RID: 45D, highlighted in yellow, exactly matches the first group from the PAC in the above example.

Every group, when created, is assigned an unique SID and the SID contains the relative groupid.

Windows has predefined a set of relative group id’s (a sub-set of the complete list).

Entity

RID

Type

Domain Administrator

500

User

Domain Guest

501

User

Domain Admins

512

Group

Domain Users

513

Group

Domain Guests

514

Group

 

More predefined entities can be found in [5].

Note that all users which member of the domain (forest/tree) will be member of the RID: 513, the Domain Users. Decimal 513 equals HEX 0x201 which exactly matches the last group in our PAC group membership decoding example above.

The SID logonDomain contains info on the objectSID of the domain which the user is logged on to.

01B0: 04 00 00 00 01 04 00 00 - 00 00 00 05 15 00 00 00 ................
01C0: E6 E2 FB 72 95 19 4C 81 - B9 68 4F C6 00 00 00 00 ...r..L..hO.....

The SID is constructed of the parts of the objectSid which refers to the non-relative id described above.

Above example have 4 sub-object ids: 0x15, 0x72fbe2e6, 0x814c1995, 0xc64f68b9.

If we look at the above example, we see that these sub-object ids matched exactly the object id of the user: “S-15-72fbe2e6-814c1995-c64f68b9”.

> objectSid: S-15-72FBE2E6-814C1995-C64F68B9-45D;
 

PAC in a HTTP world

Now, the PAC is decoded and we have the list of objectSid’s that the user is member of.

We could now do two things: Use the ObjectSid’s as the j2ee security roles directly or map the objectSid’s to more descriptive and user friendly names.

The open source SAMBA project maintains a mapping between the relative object ID and the UNIX groups. It’s done using a simple table lookup to do the mapping.

If we define a similar mapping, let’s say in a pac-j2ee.properties file we could have:

# Mapping file for SIDs to J2EE roles
domain.dc.TEST.objectSid=S-15-72FBE2E6-814C1995-C64F68B9
objectSid.S-15-72FBE2E6-814C1995-C64F68B9-483=universalgroup@TEST
objectSid.S-15-72FBE2E6-814C1995-C64F68B9-45D=spnegousers@TEST
objectSid.S-15-72FBE2E6-814C1995-C64F68B9-47C=haba1@TEST
objectSid.S-15-72FBE2E6-814C1995-C64F68B9-47D=haba2@TEST
objectSid.S-15-72FBE2E6-814C1995-C64F68B9-461=haba@TEST
objectSid.S-15-72FBE2E6-814C1995-C64F68B9-200=Domain Admins@TEST
objectSid.S-15-72FBE2E6-814C1995-C64F68B9-201=Domain Users@TEST
objectSid.S-15-72FBE2E6-814C1995-C64F68B9-202=Domain Guests@TEST
domain.dc.SUB.objectSid=S-15-B8D92021-3A33E28E-75372434
objectSid.S-15-B8D92021-3A33E28E-75372434-456=spnegousers@SUB
objectSid.S-15-B8D92021-3A33E28E-75372434-200=Domain Admins@SUB
objectSid.S-15-B8D92021-3A33E28E-75372434-201=Domain Users@SUB
objectSid.S-15-B8D92021-3A33E28E-75372434-202=Domain Guests@SUB

The above example shows how objectSid’s which are unique across Active Directory forest are mapped in a multi domain setup. Above we have two domains: TEST and a sub domain SUB.

Note that a user can be member of groups in both domains.

When a user is authenticated, the PAC will contain a list of objectSid’s which is mapped to j2ee security roles using the above mapping. The j2ee security role names can be chosen freely. The doesn’t even have to be unique. Multiple objectSid’s can be mapped to the same j2ee security role.

A typical J2EE security enabled deployment descriptor specifies the role names as long descriptive names.

 <security-constraint>
 <web-resource-collection>
 <web-resource-name>
 Restricted Area
 </web-resource-name>
 <url-pattern>/protectedurl</url-pattern>
 </web-resource-collection>
 <auth-constraint>
 <role-name>spnegousers@TEST</role-name>
 </auth-constraint>
 </security-constraint>

This specifies that a J2EE resource protectedurl is protected by a J2EE security role called spnegousers@TEST.NET.

When accessing the protectedurl resource, the j2ee security manager will lookup group and user membership information on the authenticated user. The security manager will try to lookup the j2ee roles attached to the authenticated user, which originally comes from the objectSid’s inside the PAC structure.

GSSAPI SASL mechanism and the KEYTAB principal

One way to connect to Active Directory is to connect using the LDAP interface and the Global Catalog. This can be done using a system user either over an unencrypted connection or over an encrypted connection e.g. SSL.

Another way is to reuse the credentials already present to the server; the server principal credentials stored in the KEYTAB file. The KEYTAB file contains the secret encryption key that is used to decrypt the Kerberos ticket content. But exactly that encryption key is derived from the principal name and the password for the webserver account; the SPN account.

When a JNDI context is created, the connection method is defined as a parameter to the context environment. A popular mechanism is the simple mechanism, which is based on userid and password. The JNDI simple SASL mechanism will use the userid and password to authenticate towards the JNDI server.

Active Directory support the another SASL mechanism called GSSAPI. The SASL client in the JNDI context expects a Kerberized JAAS Subject populated with a Kerberos TGT. That Kerberos TGT can acquired using the encryption key from the KEYTAB file. All this leads to simple configuration, since all the configuration parameters are already present.

The PAC mapping can then use the GSSAPI SASL mechanism to map object Sid's but configuring a JAAS login module:

dk.itp.pac.auth.module {
  dk.itp.spnego.auth.module.Krb5KeyTabLoginModule required
        debug=false;
};

The PAC mapper will use the login module to acquire a Kerberized JAAS Subject and afterwards do the JNDI lookups using Subject.runAs(). The SASL client will then be able to pull the Kerberized JAAS Subject from the thread and connect securely; with confidentiality and integrity enabled.

PAC enabled SPNEGO Servlet Filter

The servlet filter is probably the simplest way of adding security to an already existing application which does not use j2ee security.

The SPNEGO-PAC servlet filter is capable of authenticating the user and getting the security roles from the mapping file. All the information is then stored on a SpnegoPrincipal. The SpnegoPrincipal class has the following public methods:

/**
 * Get the j2ee roles mapped from Active Directory Object SID's (from user-group membership).
 * @return List of Strings (j2ee roles mapped using the mapping file)
 */
public List getRoles();
 
/**
 * Get the Active Directory Object SID's (from user-group membership).
 * @return List of String (object SID's)
 */
public List getObjectSids();

These methods make it possible for an application to pull the necessary user and group information from the user object on the HttpServletRequest:

HttpServletRequest req;
SpnegoPrincipal authenticatedPrincipal = (SpnegoPrincipal)req.getUserPrincipal();

PAC enabled SPNEGO Tomcat Authenticator

The task of a tomcat authenticator plugin is to verify security credentials received from at client and to populate the J2EE security roles on the authenticated user. 

This is normally done in two steps:

  • Verify the security credentials. This normally involves looking up userid and password in a database.

  • Looking up group membership on the user account (or in a group table).

An optimized authenticator module can do this in 1 or 2 lookups per user authentication.

A PAC enabled SPNEGO authenticator can do this without accessing the Active Directory through the LDAP interface.

The SPNEGO authenticator received an SPNEGO token. This token contains the Kerberos authentication ticket [7] [8]. As described above the PAC can be decoded from the authorization-data field in the Kerberos authentication ticket.

WebLogic 8.1

The security model in WebLogic 8.1 is built around JAAS with support for Identity Asserters and Login Modules.

The Identity asserter is a provider that is capable of creating a JAAS callback handler with then can be used in the authentication and authorization modules.

In order to get this working, we need to create an Identity asserter that handles SPNEGO/Kerberos verification and sets the J2EE roles as attributes on the callback.

In our current version we (re)use the already existing LDAP based authorization module. But when using the PAC and the already populated J2EE security roles, we need to create our own authorization module that verifies access to the application roles. This module does not have to go to LDAP to do this verification. This is done completely in-memory.

As a simpler alternative the PAC enabled SPNEGO/Kerberos ServletFilter can be used if a simpler setup without declarative J2EE security is not used. Some systems are built around a custom security infrastructure. In these situations it can be easier to use the ServletFilter approach.

WebSphere 5.1

WebSphere 5.1 is built on a HTTP based plugin called the NTAI (Negotiate Trust Association Interceptor). This plugin has the responsibility of authenticating the user based on the content of the HTTP request.

The SPNEGO tokens is stored in the authorization header of HTTP request, ans the NTAI is therefore capable of authenticating the user.

However, the NTAI’s on response back to the WAS security manager is the authentication user as a single String which is the authenticated user’s userid. There is no real way of putting the J2EE security groups on the “subject”. The plugin structure is too simple for that.

The WAS also has an authorization plugin called the Realm, which only responsibility is to do lookups in a backend database answering questions like: getGroups(), getUser(), isMemberof()… The key is typically the authenticated user (as a string).

WAS works by getting all the groups for a specified userid. The list of groups and the user is then compared to the J2EE deployment descriptor to derive the final answer (access granted or access denied).

The ServletFilter approach works just like in the Tomcat and WebLogic case. WAS does not have to run with “global security = on” to use the PAC-SPNEGO/Kerberos servlet filter.

Conclusion

We have shown that it is possible to do PAC decoding of the Kerberos authentication ticket in Java. The PAC contains an authenticated user’s group membership.

The list of groups or more correct, list of object id’s can be mapped to J2EE security roles which can then be used by security plugins or in ServletFilter for various web application servers.

The architecture of providing security roles at authentication time works well with WebLogic and Tomcat security managers and plugin architectures. It does not work well with WebSphere 5.1. However, a ServletFilter seems to work very well on most (if not all) web application servers.

References

[1]

RFC 1510 The Kerberos Network Authentication Service V5, Internet Engineering Task Force (IETF), Sep. 1993. http://www.ietf.org/rfc/rfc1510.txt?number=1510

[2]

Utilizing the Windows 2000 Authorization Data in Kerberos Tickets for Access Control to Resources, Brezak, Microsoft Corporation, Feb. 2002. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnkerb/html/msdn_pac.asp

[3]

Transfer Syntax NDR, from CDE 1.1: Remote Procedure Call, The Open Group, 1997. http://www.opengroup.org/onlinepubs/9629399/chap14.htm

[4]

Jarapac – DCE/RPC in Java, Source Forge, 2004, http://jarapac.sourceforge.net/

[5]

SAMBA Project Documentation, April 2003, Chapter 12. Group Mapping MS Windows and UNIX, J.F. Micouleau, G. Carter, http://info.ccone.at/INFO/Samba/groupmapping.html#id2909853

[6]

What Is A Group, Wiki article, K.Brown, http://pluralsight.com/wiki/default.aspx/Keith.GuideBook/WhatIsAGroup.html

[7]

SPNEGO/Kerberos authentication with Apache Tomcat, 2004, Friis, http://appliedcrypto.com/spnego/tomcat_spnego.html

[8]

SPNEGO/Kerberos authentication using JGSS, 2004, Friis, http://appliedcrypto.com/portalprotect/SPNEGO%20authentication%20using%20JGSS.pdf

About the author

Bo Friis is working as a security consultant for IT Practice in Denmark. He has specialized in security protocols and implementations. He is working on security solutions for various customers. He is the co. architect and developer of the PortalProtect product and the architect and developer of SPNEGO/Kerberos product.

He has designed and developed the initial version of OpenSign and OpenLogon, a set of applets that supports digital signature using X.509 certificates over the XMLDSIG standard. The result was donated to the open source OpenOCES project.

Bo Friis holds a Masters degree in Cryptography from the University of Aarhus in Denmark. He also holds a Master of Science degree in Electrical Engineering from the Technical University of Denmark.

He can be reached at email: jbf_AT_practice.dk



(c) 2005 IT Practice

(c) copyright appliedcrypto.com AppliedCrypto News RSS feed