Security Identifiers (SID)
A security identifier (SID) is a unique value of variable length used to identify a trustee. Each account has a unique SID issued by an authority, such as a Windows domain controller, and stored in a security database. Each time a user logs on, the system retrieves the SID for that user from the database and places it in the access token for that user. The system uses the SID in the access token to identify the user in all subsequent interactions with Windows security. When a SID has been used as the unique identifier for a user or group, it cannot ever be used again to identify another user or group. Windows security uses SIDs in the following security elements:
In addition to the uniquely created, domain-specific SIDs assigned to specific users and groups, there are well-known SIDs that identify generic groups and generic users. For example, the well-known SIDs, Everyone and World, identify a group that includes all users. Most applications never need to work with SIDs. For example, in Windows NT 4.0, the security functions for getting and setting the ACEs in an ACL allow you to use names rather than SIDs to identify users and groups. Because the names of well-known SIDs can vary, you should use the functions to build the SID from predefined constants rather than using the name of the well-known SID. For example, the U.S. English version of the Windows operating system has a well-known SID named BUILTIN\Administrators that might have a different name on international versions of the system. If you do need to work with SIDs, do not manipulate them directly. Instead, use the following functions.
Function |
Description |
AllocateAndInitializeSid() |
Allocates and initializes a SID with the specified number of sub authorities. |
ConvertSidToStringSid() |
Converts a SID to a string format suitable for display, storage, or transport. |
ConvertStringSidToSid() |
Converts a string-format SID to a valid, functional SID. |
CopySid() |
Copies a source SID to a buffer. |
EqualPrefixSid() |
Tests two SID prefix values for equality. A SID prefix is the entire SID except for the last sub authority value. |
EqualSid() |
Tests two SIDs for equality. They must match exactly to be considered equal. |
FreeSid() |
Frees a previously allocated SID by using the AllocateAndInitializeSid() function. |
GetLengthSid() |
Retrieves the length of a SID. |
GetSidIdentifierAuthority() |
Retrieves a pointer to the identifier authority for a SID. |
GetSidLengthRequired() |
Retrieves the size of the buffer required to store a SID with a specified number of sub-authorities. |
GetSidSubAuthority() |
Retrieves a pointer to a specified sub authority in a SID. |
GetSidSubAuthorityCount() |
Retrieves the number of sub authorities in a SID. |
InitializeSid() |
Initializes a SID structure. |
IsValidSid() |
Tests the validity of a SID by verifying that the revision number is within a known range and that the number of sub authorities is less than the maximum. |
LookupAccountName() |
Retrieves the SID that corresponds to a specified account name. |
LookupAccountSid() |
Retrieves the account name that corresponds to a specified SID. |
Table 16 |
Interaction Between Threads and Securable Objects
An application consists of one or more processes. A process, in the simplest terms, is an executing program. One or more threads run in the context of the process. A thread is the basic unit to which the operating system allocates processor time. A thread can execute any part of the process code, including parts currently being executed by another thread. A fiber is a unit of execution that must be manually scheduled by the application. Fibers run in the context of the threads that schedule them. A job object allows groups of processes to be managed as a unit. Job objects are namable, securable, sharable objects that control attributes of the processes associated with them. Operations performed on the job object affect all processes associated with the job object. When a thread attempts to use a securable object, the system performs an access check before allowing the thread to proceed. In an access check, the system compares the security information in the thread's access token against the security information in the object's security descriptor. The access token contains SIDs that identify the user associated with the thread.
The security descriptor identifies the object's owner and contains a DACL. The DACL contains ACEs, each of which specify the access rights allowed or denied to a specific user or group. The system checks the object's DACL, looking for ACEs that apply to the user and group SIDs from the thread's access token. The system checks each ACE until access is either granted or denied or until there are no more ACEs to check. Conceivably, an ACL could have several ACEs that apply to the token's SIDs and, if this occurs, the access rights granted by each ACE accumulate. For example, if one ACE grants read access to a group and another ACE grants write access to a user who is a member of the group, the user can have both read and write access to the object. The following illustration shows the relationship between these blocks of security information.
DACLs and ACEs
The situations encountered when checking the ACEs in DACLs are listed below:
In most cases, you can control access to an object by using access-allowed ACEs; you do not need to explicitly deny access to an object. The exception is when an ACE allows access to a group and you want to deny access to a member of the group. To do this, place an access-denied ACE for the user in the DACL ahead of the access-allowed ACE for the group. Note that the order of the ACEs is important because the system reads the ACEs in sequence until access is granted or denied. The user's access-denied ACE must appear first; otherwise, when the system reads the group's access allowed ACE, it will grant access to the restricted user.
How DACLs Control Access to an Object
When a thread tries to access a securable object, the system either grants or denies access. If the object does not have a DACL, the system grants access; otherwise, the system looks for ACEs in the object's DACL that apply to the thread. Each ACE in the object's DACL specifies the access rights allowed or denied for a trustee, which can be a user account, a group account, or a logon session. The system compares the trustee in each ACE to the trustees identified in the thread's access token. An access token contains SIDs that identify the user and the group accounts to which the user belongs. A token also contains a logon SID that identifies the current logon session. During an access check, the system ignores group SIDs that are not enabled. Typically, the system uses the primary access token of the thread that is requesting access. However, if the thread is impersonating another user, the system uses the thread's impersonation token. The system examines each ACE in sequence until one of the following events occurs:
The following illustration shows how an object's DACL can allow access to one thread while denying access to another.
For Thread A, the system reads ACE 1 and immediately denies access because the access-denied ACE applies to the user in the thread's access token. In this case, the system does not check ACEs 2 and 3. For Thread B, ACE 1 does not apply, so the system proceeds to ACE 2, which allows write access, and ACE 3 which allows read and execute access. Because the system stops checking ACEs when the requested access is explicitly granted or denied, the order of ACEs in a DACL is important. Note that if the ACE order were different in the example, the system might have granted access to Thread A. For system objects, the operating system defines a preferred order of ACEs in a DACL.
Order of ACEs in a DACL
When a process tries to access a securable object, the system steps through the ACEs in the object's DACL until it finds ACEs that allow or deny the requested access. The access rights that a DACL allows a user could vary depending on the order of ACEs in the DACL. Consequently, the Windows XP/Windows 2000/Windows NT operating systems define a preferred order for ACEs in the DACL of a securable object. The preferred order provides a simple framework that ensures that an access-denied ACE actually denies access. On Windows Server 2003, Windows XP, and Windows 2000, the proper order of ACEs is complicated by the introduction of object-specific ACEs and automatic inheritance. The following steps describe the preferred order:
Take note that not all ACE types are required in an ACL. Functions such as AddAccessAllowedAceEx() and AddAccessAllowedObjectAce() add an ACE to the end of an ACL. It is the caller's responsibility to ensure that the ACEs are added in the proper order. On Windows NT 4.0 and earlier, the preferred order of ACEs is simple: In a DACL, all access-denied ACEs should precede any access-allowed ACEs. The SetEntriesInAcl() function creates a DACL with ACEs in this order. However, the low-level functions for adding ACEs to a DACL do not enforce the preferred order. The AddAce() function adds ACEs at a specified location in an ACL. Functions such as AddAccessAllowedAce() add an ACE to the end of an ACL. It is the caller's responsibility to ensure that the ACEs are added in the preferred order. The following illustration shows how the same ACEs can allow different access rights, depending on their order in the DACL. The system denies access to Object A when it reads the access-denied ACE, but the out-of-order DACL for Object B causes the system to grant access without reading the access-denied ACE.
ACEs to Control Access to an Object's Properties
The DACL of a directory service (DS) object can contain a hierarchy of ACEs, as follows:
Within this hierarchy, the rights granted or denied at a higher level apply also to the lower levels. For example, if an object-specific ACE on a property set allows a trustee the ADS_RIGHT_DS_READ_PROP right, the trustee has implicit read access to all of the properties of that property set. Similarly, an ACE on the object itself that allows ADS_RIGHT_DS_READ_PROP access gives the trustee read access to all of the object's properties. The following illustration shows the tree of a hypothetical DS object and its property sets and properties.
Suppose you want to allow the following access to the properties of this DS object:
To do this, set the ACEs in the object's DACL as shown in the following table.
Trustee |
Object GUID |
ACE type |
Access rights |
Group A |
None |
Access-allowed ACE |
ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP |
Everyone |
Property Set 1 |
Access-allowed object ACE |
ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP |
Everyone |
Property C |
Access-allowed object ACE |
ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP |
Table 17 |
The ACE for Group A does not have an object GUID, which means that it allows access to all the object's properties. The object-specific ACE for Property Set 1 allows everyone access to Properties A and B. The other object-specific ACE allows everyone access to Property C. Note that although this DACL does not have any access-denied ACEs, it implicitly denies Property D access to everyone except Group A. When a user tries to access an object's property, the system checks the ACEs, in order, until the requested access is explicitly granted, denied, or there are no more ACEs, in which case, access is implicitly denied. The system evaluates:
The system ignores object-specific ACEs that apply to other property sets or properties.