Understanding SIDs

[Previous] [Next]

SIDs are integral to Windows 2000 security. SIDs have already been touched on in this chapter and will appear again in subsequent chapters, but now is the time to look at them in detail. Here is what you know so far: SIDs are security identifiers, which identify trustees to the system. A SID is a unique binary representation of a trustee.

Here are a few common tasks you'll become familiar with:

  • Converting a user's or group's textual name to a SID.
  • Converting a SID to the textual name for the trustee.
  • Building a well-known SID from scratch. These SIDs exist on all systems and indicate accounts such as Guest or Everybody.
  • Copying a SID structure.
  • And finally, converting to and from binary and string representations of a SID.

A SID is a security identifier, a structure that identifies a trustee to the system. A SID is composed of a 48-bit value followed by a variable number of 32-bit components. In documentation, a SID is typically represented in the SRI-S-S… format. (See the sidebar.)

SID Format

A SID is typically formatted as: S-R-I-S-S…

where:

S is written literally as "S" and indicates that this series of numbers is a SID.

R is the SID's revision level, indicated by a number (currently 1).

I is a 48I-bit number indicating the authority.

S is a 32-bit number indicating a subauthority, also known as a relative identifier, or RID.

S is another subauthority. There can be any number of subauthorities in a SID.

Example : S-1-5-11

The revision number of the SID structure has been 1 since Windows NT 3.1; however, it is possible that the structure will change and we will one day see a revision level of 2. The authority "number" indicates under whose authority the SID was created—that is, what "entity" owns the SID and manages its account. Table 9-6 provides a list of currently supported authorities.

Table 9-6. SID authorities

Authority Definition
SECURITY_NULL_SID_AUTHORITY Defined as 0, this authority is used in building a SID indicating a null group or nobody.
SECURITY_WORLD_SID_AUTHORITY Defined as 1, this authority is used in building the well-known group account Everybody.
SECURITY_LOCAL_SID_AUTHORITY Defined as 2, this authority is used in building the well-known group account LOCAL.
SECURITY_CREATOR_SID_AUTHORITY Defined as 3, this authority is used with the well-known Creator Owner SID.
SECURITY_NON_UNIQUE_AUTHORITY Defined as 4, this authority is used in creating SIDs that are not unique. The first subauthority is always set to SECURITY_NT_NON_UNIQUE, which is defined as 0x15.
SECURITY_NT_AUTHORITY Defined as 5, this authority is used by user and group accounts created by Windows 2000 systems.

The subauthority is a 32-bit value that is unique relative to the authority of the SID. The next subauthority is unique relative to the previous subauthorities and finally the authority of the SID. Subauthorities are also known as RIDs.

Table 9-7 shows some well-known RIDs. (For a complete list, see WinNT.h in the Platform SDK documentation.) Remember that unlike the authorities listed previously, these RIDs are not all unique. This is because they are only unique relative to an authority and, as such, have no meaning independent of the authority.

Table 9-7. Some well-known RIDs

RID Relevant Authority Definition
SECURITY_NULL_RID NULL Defined as 0, this is the single subauthority for the well-known group NULL.
SECURITY_WORLD_RID WORLD Defined as 0, this is the single subauthority for the well-known group Everyone.
SECURITY_CREATOR_OWNER_RID CREATOR Defined as 0, this is the single subauthority for the well-known user Creator Owner.
SECURITY_CREATOR_GROUP_RID CREATOR Defined as 1, this is the single subauthority for the well-known user Creator Group.
SECURITY_CREATOR_OWNER_SERVER_RID CREATOR Defined as 2, this is the single subauthority for the well-known user Creator Owner Server.
SECURITY_CREATOR_GROUP_SERVER_RID CREATOR Defined as 3, this is the single subauthority for the well-known user Creator Group Server.

By combining a well-known RID with its respective authority, you can create a well-known SID. Well-known SIDs identify users and groups that are defined by the system and are known by every installation of Windows 2000 on every network. Table 9-8 lists some well-known SIDs and their uses.

Table 9-8. Well-known SIDs and their uses

SID Common Name Use
S-1-0-0 Null SID Indicates an empty or NULL group. It is defined as a group with no users and is typically used to indicate nobody.
S-1-1-0 Everyone Indicates a group of which all trustees are implicitly members. This is a very important SID and can be useful in creating access lists for securable objects in the system. It is also known as the World SID or World group.
S-1-2-0 Local SID Indicates a group that includes all users who log on to a system locally or physically.
S-1-3-0 Creator Owner SID Acts as a placeholder for the creator of an object. It is used with inheritable access-control lists. You will find more information on this SID in Chapter 10.
S-1-3-1 Creator Group SID Acts as a placeholder for the primary group of the creator of an object. It is used with inheritable access-control lists. You will find more information on this SID in Chapter 10.
S-1-5-1 Dialup Indicates a group of which all user accounts are automatically made members while they are logged on to a Windows 2000 system via dialup.
S-1-5-2 Network Indicates a group of which all user accounts are automatically made members while they are logged on to a Windows 2000 system via the network.
S-1-5-3 Batch Indicates a group of which all user accounts are automatically made members while they are logged on to a Windows 2000 system via a batch logon.
S-1-5-4 Interactive Indicates a group of which all user accounts are automatically made members while they are logged on to a Windows 2000 system via an interactive logon.
S-1-5-6 Service Indicates a group of which all user accounts are automatically made members while they are logged on to a Windows 2000 system as a service.
S-1-5-7 AnonymousLogon Associated with a null session logon.
S-1-5-9 ServerLogon Associated with a domain controller account.
S-1-5-10 Self (or Principal Self) Acts as a placeholder and applies only to the access lists of group or user accounts. When present, it indicates the trustee account for which the access list applies.
S-1-5-11 Authenticated User Indicates a well-known group of which all currently authenticated user accounts are implicitly members.
S-1-5-13 Terminal Server Associated with a user logged on to a terminal server.
S-1-5-18 LocalSystem This special account, which many service processes run under, exists on all Windows 2000 systems. For more information, see Chapter 11.

You will see some of these well-known SIDs only occasionally, and others crop up quite often. It is good to be familiar with these built-in trustees of the system.

The SID data structure itself is defined as follows:

 typedef struct _SID {    BYTE  Revision;    BYTE  SubAuthorityCount;    SID_IDENTIFIER_AUTHORITY IdentifierAuthority;    DWORD SubAuthority[ANYSIZE_ARRAY]; } SID; 

Each member of this structure should now be familiar to you. ANYSIZE_ARRAY is defined as 1, primarily to indicate that the structure does not necessarily end with only a single DWORD representing a subauthority. Although the SID structure itself is very clear, it is an "opaque" data structure and should be manipulated only by using the system functions provided. This opacity affords the developers at Microsoft the freedom to change the internal structure of the SID in the future. Your software should follow this rule to a fault.

Building SIDs

As a rule, you will be building SIDs for well-known trustees such as the Everyone group. You build a SID by looking up the authority and trustee values for the trustee in question and passing them to the AllocateAndInitializeSid function:

 BOOL AllocateAndInitializeSid(    PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority,    BYTE  nSubAuthorityCount,    DWORD dwSubAuthority0,    DWORD dwSubAuthority1,    DWORD dwSubAuthority2,    DWORD dwSubAuthority3,    DWORD dwSubAuthority4,    DWORD dwSubAuthority5,    DWORD dwSubAuthority6,    DWORD dwSubAuthority7,    PSID* ppSid); 

You should recognize the purpose of each of these parameters, but some points are worth mentioning. The pIdentifierAuthority parameter identifies the authority value for the SID you are building. The pIdentifierAuthority parameter is of type SID_IDENTIFIER_AUTHORITY, which is defined as an array of 6 bytes. This might seem a bit awkward, but thankfully the Platform SDK documentation defines the useful SID authorities. You should pass one of the values shown in Table 9-6, such as SECURITY_NT_AUTHORITY.

The second parameter, nSubAuthorityCount, indicates the number of subauthorities that you require for the SID. Although the system has defined the value SID_MAX_SUB_AUTHORITIES as 15, AllocateAndInitializeSid will only build SIDs with 8 or fewer subauthorities.

NOTE
Regardless of the limitations of AllocateAndInitializeSid, you should continue to treat SIDs as though they can be any size now and in the future (that is, avoid static buffers).

The next eight parameters indicate the subauthorities for your new SID. I've often wondered why the developers at Microsoft didn't choose to consolidate these parameters into a single pointer to an array of DWORDs. Regardless, you should pass relevant values for as many subauthorities as you need and pass 0 for the remaining parameters.

The final parameter, ppSid, is worthy of extra notice. It is defined as PSID*, which is a pointer to a pointer to a SID. Unlike most functions provided by Windows, AllocateAndInitializeSid allocates a buffer for you, rather than expecting you to provide a buffer of sufficient length. The address of the allocated memory is returned via this parameter.

If AllocateAndInitializeSid returns FALSE, most likely you have passed a subauthority count larger than 8. Because the function allocates memory for you, another cause of failure can be low memory.

If AllocateAndInitilizeSid succeeds, the PSID variable will contain a pointer to your new SID. When you are finished with the SID, you should pass it to FreeSid so that the system can free the memory used by the SID:

 PVOID FreeSid(PSID pSid); 

Using AllocateAndInitializeSid, you can build well-known SIDs as well as dynamic SIDs for which your software knows the authority and subauthority values. The following example shows how to use this function to build a SID representing the well-known group Everyone:

 PSID BuildEveryoneSid() {    SID_IDENTIFIER_AUTHORITY auth = SECURITY_WORLD_SID_AUTHORITY;    PSID pSID = NULL;    BOOL fSuccess = AllocateAndInitializeSid(&auth, 1,       SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSID);    return(fSuccess ? pSID : NULL); } 

Once you are finished with the SID returned by this function you would naturally pass its pointer to the FreeSid function. Notice that you can easily modify this function to build another well-known SID by changing the initialization value of the auth variable and passing a different RID define to AllocateAndInitializeSid.

Trustee Name and Binary SID Conversion

Two useful tasks you will commonly need to perform are conversions to and from trustee account names and binary SIDs. The system provides two functions to perform these tasks. These functions have potentially confusing names, so here is the best way to remember which is which:

  • If you have the account name of a trustee but you want a binary SID, call LookupAccountName.
  • Conversely, if you have a binary SID but you want the trustee account name, call LookupAccountSid.

As you can see, the functions have the name of the information you already have.

Retrieving a Trustee's Binary SID

LookupAccountName is prototyped as follows:

 BOOL LookupAccountName(    PCTSTR        pszSystemName,    PCTSTR        pszAccountName,    PSID          pSid,    PDWORD        pcbSid,    PTSTR         pszReferencedDomainName,    PDWORD        pcbReferencedDomainName,    PSID_NAME_USE peUse); 

The pszSystemName parameter should be the name of the system on which you wish to look up the name specified in the pszAccountName parameter. You can pass NULL for the pszSystemName parameter to indicate the local machine.

You should pass a pointer to a buffer in memory large enough to hold the requested SID as the pSid parameter, as well as the address of a DWORD containing the size of your buffer as the pcbSid parameter. If your buffer is too small, the system will return failure and the GetLastError function will return ERROR_INSUFFICIENT_BUFFER. LookupAccountName will also fill the DWORD variable you supplied via the pcbSid parameter with the size the buffer should be.

The pszReferencedDomainName and pcbReferencedDomainName work similarly for a buffer to receive the domain name associated with the account and for the pointer to a DWORD variable containing the size, respectively. You cannot pass NULL for either of these values, even if the trustee's associated domain name is not required.

You should pass the address of a SID_NAME_USE variable as the peUse parameter. LookupAccountName will return a value, in the variable pointed to by peUse, indicating the type of SID it has returned to you. Table 9-9 lists the possible values.

Table 9-9. SID_NAME_USE enumeration type values

Value Use
SidTypeUser Indicates a SID for a user trustee account
SidTypeGroup Indicates a SID for a group trustee account
SidTypeDomain Indicates a SID representing a domain object
SidTypeAlias Indicates a SID representing a built-in group such as the Administrators group
SidTypeWellKnownGroup Indicates a SID for a well-known group such as the Everyone group
SidTypeDeletedAccount Indicates a SID for a deleted account
SidTypeInvalid Indicates an invalid SID
SidTypeUnknown Indicates an unknown SID type
SidTypeComputer Indicates a SID for a computer account on the domain

Although LookupAccountName takes a computer name as a parameter, its search is not limited to the machine indicated. The search should be thought of as being performed from the perspective of the machine indicated. That said, the following list shows the search order for a trustee:

  1. Well-known SID names
  2. Built-in and defined accounts on the local machine
  3. The primary domain of the system
  4. Trusted domains
  5. Any domain in a domain forest

NOTE
Although you can use LookupAccountName to find well-known SIDs, you should always use AllocateAndInitializeSid to directly build well-known SIDs.

As you can see, the namespace for trustees is shared among users, groups, computers, and domains. Although the system will not allow you to create a user or a group with the same name as another user or group, the system does allow a user or a group to have the same name as a computer or a domain. This creates potential problems, because LookupAccountName will return the SID for a computer before it returns the SID for a user.

Your software must account for the possibility of a user or a group having the same name as a computer or a domain. The best way to deal with this is to explicitly indicate the computer name for the account as part of the account name. For example, if both a computer and a user are named "JClark", and the "JClark" account exists on the "JClark" computer, the following code will return a SID for the user account:

 BOOL fRet = LookupAccountName(NULL, "JClark\\JClark",    &sid, &dwSizeSid, szBuffer, &dwSizeBuf, &use); 

Retrieving a Trustee's Account Name

If your software needs to find the account name for a trustee when it already has a binary SID, it should use LookupAccountSid:

 BOOL LookupAccountSid(    PCTSTR        pszSystemName,    PSID          pSid,    PTSTR         pszName,    PDWORD        pcbName,    PTSTR         pszReferencedDomainName,    PDWORD        pcbReferencedDomainName,    PSID_NAME_USE peUse); 

The pszSystemName parameter is the name of the machine from which you perform the search. When LookupAccountSid searches for a trustee name, it follows the same search order and rules as does LookupAccountName.

The pSid parameter is a pointer to an existing SID for which you want to retrieve the corresponding account name. This SID can be any valid SID built by you or previously returned by the system in a token or some other structure.

The pszName and pszReferencedDomainName should point to buffers that you provide to receive the trustee name and its domain name from the system. These buffers should be large enough to hold the return values; their sizes are passed via pointers to DWORD variables, where pcbName indicates the length of the buffer pointed to by pszName, and pcbReferencedDomainName indicates the length of the buffer pointed to by pszReferencedDomainName. If you do not pass buffers of sufficient size to receive these names from the system, the system returns the required buffer sizes in these two variables.

You should pass the address of a SID_NAME_USE variable for peUse to receive from the system the use for the SID. For an explanation of the SID_NAME_USE enumeration type, see Table 9-9.

Copying SIDs

Many system functions return SIDs, and likewise many security-related functions expect you to build structures including SIDs or pointers to SIDs. This wouldn't be a problem except that SID structures are variable in length and the system designers have asked us to treat the structure as opaque. Luckily, the system provides a function to copy a SID:

 BOOL CopySid(    DWORD dwDestinationSidLength,    PSID  pSidDestination,    PSID  pSidSource); 

This easy-to-use function simply takes the length of your buffer (in bytes), a pointer to a destination SID, and a pointer to the original SID.

Although the system knows the length of the SID that it is copying, it still needs to be sure that it does not write past the length of your buffer. This is why CopySid requires that you pass a length as the first parameter. More importantly, you will need to know how large a buffer to allocate. You can find the length of a SID in bytes using the GetLengthSid function:

 DWORD GetLengthSid(PSID pSid); 

Using the value returned from this function, you can allocate a buffer for your new SID.

Textual and Binary SID Conversion

At this point, you know how to perform the most commonly needed tasks concerning SIDs. However, our discussion of SIDs wouldn't be complete unless I told you how to convert a textual representation of a SID to a binary representation and vice versa.

Before proceeding, let me clarify what I mean by "textual SID." I am not referring to the string representing the account name of the trustee; rather, I am referring to the string representing the binary SID structure. For example, "S-1-1-0" is a textual SID representing the SID whose trustee account name is Everyone.

Textual SIDs can be useful when storing SID names in persistent storage, such as the system registry, or when representing a SID in a user interface. The functions you use to convert to and from textual SIDs are ConvertSidToStringSid and ConvertStringSidToSid, respectively. Here are their prototypes:

 BOOL ConvertSidToStringSid(    PSID   pSid,    PTSTR* StringSid); BOOL ConvertStringSidToSid(    PCTSTR StringSid,    PSID*  ppSid); 

Both of these functions allocate buffers for you, and it is your job to free them when you are finished with the returned data. Use the LocalFree function to free the buffers returned by these functions.

Now that you have an exhaustive understanding of the system's binary identification for trustee accounts, you have all the information necessary to exploit both methods of manipulating membership of local groups as described earlier in this chapter. So let's begin our discussion on assigning and removing privileges for trustee accounts.



Programming Server-Side Applications for Microsoft Windows 2000
Programming Server-Side Applications for Microsoft Windows 2000 (Microsoft Programming)
ISBN: 0735607532
EAN: 2147483647
Year: 2000
Pages: 126

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net