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:
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
|