Lately I’ve been working on a native Java implementation of Waffle and contributing an increasing amount of code to the java.net JNA project. Waffle has several goals that provide support for Windows authentication in Java. In this post I’ll describe the JNA implementation that allows us to retrieve user group memberships for the currently logged on user or a user logged on with a username and password. Both of these were made really simple by JNA.
A user’s security context is represented by a token and includes local and domain groups flattened out. That is, it contains all groups, including nested ones. If this construct didn’t exist, Windows would have to call Active Directory every time it wants to, for example, verify whether a logged on user has access to a certain file. Having the group memberships in the security context allows the operating system to compare the group SIDs with the values in the file’s ACL.
Incidentally, this concept has additional magical powers that we developed in one of my Microsoft patents, that allows you to inject a random group SID at logon by writing some SSPI code (a real brain teaser) for a group that doesn’t exist. You get a special SID for Passport accounts and hence can grant access to Passport users that have never logged on a server by looking up a user by e-mail and deriving a Passport SID. When the user actually logs on the server, he inherits a Passport SID in the list of group SIDs, and voila. This is how Password authentication works in Windows Longhorn. But I digress…
Asking the security context about group memberships is hence is the “correct” way of retrieving this information. I’ve seen many painfully complicated code that attempt to lookup user group memberships by enumerating groups, first local, then domain and even tries to get nested ones. This is a difficult process that almost never leads to correct results, especially in scenarios with Active Directory trusts.
Currently Logged-On User: security token
What we want is a security token for the current thread or, if that doesn’t exist, of the current process. This is the token that gives you access to the information such as which user created the thread or process (self) and hence which security groups are attached to it.
Notice that this kind of code is almost identical to .NET PInvoke. JNA makes it completely trivial to wrap a DLL function.
Currently Logged-On User: security groups
Now that we’re holding a security token, we can get the security groups. This is done with Advapi32.dll’s GetTokenInformation. The latter is a little tricky because the Win32 API is capable of retrieving all kinds of token information and takes a pointer to a buffer in memory. But in Java we want to get strongly typed Structure objects back. JNA simplifies this and makes pointer and structure virtually interchangeable by doing all the marshalling logic under the hood.
We can derive TOKEN_GROUPS
from Structure
and pass it as tokenInformation
.
Notice that the number of SID_AND_ATTRIBUTES
is unknown. When GetTokenInformation
returns it puts the number of these items into GroupCount
. JNA supports Structure.toArray
that just says “this is not a structure, it’s an array of structures”. This is similar to .NET’s PInvoke Marshal.PtrToStructure
, albeit slightly more hacky because what we get back from Win32 is a pointer to an array, not a structure.
Finally, we can call GetTokenInformation
, the first time to obtain the size of the memory required and the second time to get the actual data.
Logon a User: security groups
The only difference of obtaining the security groups of a user that we logon with a username and password is that the user’s token is returned by LogonUser
.
JNA Platform Library: making it easy, just for you
In the current JNA source you’ll find a few changes in the distribution structure: examples.jar is gone and a new platform.jar was added. Demos are built separately into a contrib folder. Platform-specific APIs, such as the ones described above, go into platform.jar. Win32 APIs are in com.sun.jna.platform.win32
. There’re several classes that support the goals described above.
com.sun.jna.platform.win32.Advapi32
: pure Advapi32.dll interfacescom.sun.jna.platform.win32.Advapi32Util
: a utility library that makes Advapi32.dll interfaces usable without too much hassle
I’ve added some useful functions to Advapi32Util:
convertSidToStringSid
: convert from a SID to a stringconvertSidStringToSid
: convert from a string to a SIDgetAccountByName
: retrieving account information by account name, usingLookupAccountName
getAccountBySid
: retrieve account information by SID, usingLookupAccountSid
getCurrentUserGroups
: get groups for the currently logged on usergetTokenAccount
: get information about the user who owns a tokengetTokenGroups
: get groups for a give token (a user logged on withLogonUser
)