Logon ID SIDs

I've mentioned logon ID SIDs a couple of times, but they're fairly arcane. I first ran into them when I was exploring just what was in a process token, and a group SID came up that I wasn't familiar with. Here's how a SID is defined:

typedef struct _SID {

BYTE Revision;

BYTE SubAuthorityCount;

SID_IDENTIFIER_AUTHORITY IdentifierAuthority;

DWORD SubAuthority[ANYSIZE_ARRAY];

} SID, *PISID;

 

The Revision is always set to 1, or if you want to be careful, set it to SID_REVISION. The SubAuthorityCount field tells you how many SubAuthority values are present, so the size of a SID should always be:

Sizeof(SID) + SubAuthorityCount * sizeof(DWORD) – sizeof(DWORD)

Note that I didn't write ( SubAuthorityCount -1 ), because if someone managed to give you an illegal SID with 0 SubAuthorities, we'd have an int overflow. Legal values for SubAuthorityCount are 1-15. If you're building ACLs by hand, it's often convenient to just declare a buffer large enough to hold the largest legal SID on the stack, and not have to mess around with allocating things.

Practically, we can often tell a lot from the SubAuthorityCount – if it is 1, you're usually looking at a well known group, such as everyone, and a handy thing to do is to just declare something like this, like so:

SID EveryoneSid = {SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, 0};

If you have 2 SubAuthority values, then you're typically dealing with a built-in group, and the first value (or RID, for relative ID) will be 0x20, and the next might be 0x221, which is BUILTIN\Users.

A SID with 4 RIDs is typically an account domain, and the last 3 of these uniquely identify that domain. Individual systems also have an account domain SID. If you can look this up, and then start tacking on a 5th RID, and then do a reverse lookup, you can enumerate the users on the system, and you can even enumerate the users on the domain through one of the workstations attached to the domain. In the last few years, this has largely been foiled by restrictions on anonymous lookups and the fact that domains can now have very large gaps in the user RIDs that get passed out.

Service SIDs are unique to a service account on a given system, and have 6 RIDs, which is new in Vista, and the first time I've noticed us using a 6 RID SID.

The IdentifierAuthority is an array of 6 bytes, and so far we've only ever used one of them, with only about 10 distinct values among those.

A logon ID SID has 3 RIDs, which is fairly unusual. You can tell if you're dealing with a logon ID SID if SubAuthority[0] == SECURITY_LOGON_IDS_RID (or 5), and SubAuthorityCount == SECURITY_LOGON_IDS_RID_COUNT. These get uniquely generated for each logon session, but if you logon as one user, then do a RunAs to another user, both processes end up with the same logon ID SID. If you call LogonUser, you get a new and unique logon ID SID every time.

Why this matters is that one of the only places you'll see something ACL'd to a logon ID SID is the window station and the desktop. You'd prefer not to make anything persistent ACL'd to the logon ID SID, since if you logged off and back on, you wouldn't have access to the object any longer. But it can be useful when setting an ACL to allow access based on any process running in this logon session. If you disabled it in your restricted token, you couldn't use it to run a windowed process (though command line apps still work).

The logon ID SID is also handy if you need to share some dynamic resource with the rest of the apps running on the same desktop, but something to look out for is that prior to Vista, all sorts of things ended up in session 0 – the console user, services, and scheduled tasks. If you made something with a specific name, then using the logon ID SID could run into problems in session 0, since you have processes with different logon ID SIDs.

Next, I'll move on to how we can further restrict processes once we have the restricted token built.