At work I’ve been writing some code to do some basic interactions with the Windows security subsystem, such as testing if a user has the Log On As a Service privilege. Every time I venture in this area of Windows I cringe, because the security APIs are disjointed, incoherent, and vastly over-complicated. Presumably this is due to the evolution of the Windows security subsystem as it diverged from its VMS roots, but that’s no excuse.
The latest example pertains to the ‘Log on as a service’ privilege, called
SeServiceLogonRight, which corresponds to a
#define in the Windows SDK called
SE_SERVICE_LOGON_NAME. I need to know if a given account has this privilege, because if not I need to grant it before the account can run a service. Easy, right?
You’d think so. There’s an API function,
PrivilegeCheck, that will test if a given token has a set of privileges. The only problem is that
PrivilegeCheck wants the privileges described as
LUIDs, which are the binary identity of each privilege. All I know is the so-called “programmatic name”, that is,
SeServiceLogonRight. No problem; there’s an API for that, called
LookupPrivilegeValue, that returns the LUID for a privilege given its name.
So I wrote that up, but something’s wrong.
LookupPrivilegeValue is failing with error
ERROR_NO_SUCH_PRIVILEGE (that’s error number
1313). The text associated with that error code is
A specified privilege does not exist. This is, of course, bullshit, since I got the priv name from the Windows headers.
After a bit of spelunking, though, I found this MSDN document.aspx) that notes certain privileges, including
SeServiceLogonRight as well as other favorites like
SeNetworkLogonRight, are so-called “account rights”. What’s the difference between an “account right” and normal privileges? Well:
All of the LSA functions mentioned in the introduction above support both account rights and privileges. Unlike privileges, however, account rights are not supported by the LookupPrivilegeValue and LookupPrivilegeName functions.
Nice. So, how the hell do I test for this privilege–I mean–account right?
The LsaEnumerateAccountRights function enumerates the account rights held by a specified account.
Lovely. The reason that sucks is that I can pass a token from
LsaEnumerateAccountRights wants a SID, plus I have to open a handle to the local security policy. Why the FUCK are account rights and privileges treated as equivalent if they have different semantics and APIs? And don’t get me started on the epic FAIL that is