Understanding Privileges and Account Rights

[Previous] [Next]

A privilege is a right assigned to a trustee that has system-wide ramifications. (In contrast, an access right assigned to a trustee defines access allowed to a specific object in the system.) Examples of privileges include the rights to log on locally, back up files and directories, and shut down the system.

Another way of looking at a privilege is as a way to secure a system function. For example, certain system functions, such as LogonUser and InitiateSystemShutdown, require that the calling software have the proper privileges or else the function will fail.

Privileges have two textual representations you should be aware of. These representations are the friendly name that shows up in the user interface for Windows and the programmatic name used by your software. Each of the programmatic names is defined in a header file in the SDK.

Table 9-10 lists all the privileges available in Windows 2000 at the time of this book's printing. A partial list is given in the Platform SDK documentation.

Table 9-10. Privileges and account rights

Programmatic Name Display Name Header File
SeAssignPrimaryTokenPrivilege
SE_ASSIGNPRIMARYTOKEN_NAME
Replace a process level token WinNT.h
SeAuditPrivilege
SE_AUDIT_NAME
Generate security audits WinNT.h
SeBackupPrivilege
SE_BACKUP_NAME
Back up files and directories WinNT.h
SeChangeNotifyPrivilege
SE_CHANGE_NOTIFY_NAME
Bypass traverse checking WinNT.h
SeCreatePagefilePrivilege
SE_CREATE_PAGEFILE_NAME
Create a pagefile WinNT.h
SeCreatePermanentPrivilege
SE_CREATE_PERMANENT_NAME
Create permanent shared objects WinNT.h
SeCreateTokenPrivilege
SE_CREATE_TOKEN_NAME
Create a token object WinNT.h
SeDebugPrivilege
SE_DEBUG_NAME
Debug programs WinNT.h
SeEnableDelegationPrivilege
SE_ENABLE_DELEGATION_NAME
Enable computer and user accounts to be trusted for delegation WinNT.h
SeIncreaseBasePriorityPrivilege
SE_INC_BASE_PRIORITY_NAME
Increase scheduling priority WinNT.h
SeIncreaseQuotaPrivilege
SE_INCREASE_QUOTA_NAME
Increase quotas WinNT.h
SeLoadDriverPrivilege
SE_LOAD_DRIVER_NAME
Load and unload device drivers WinNT.h
SeLockMemoryPrivilege
SE_LOCK_MEMORY_NAME
Lock pages in memory WinNT.h
SeMachineAccountPrivilege
SE_MACHINE_ACCOUNT_NAME
Add workstations to domain WinNT.h
SeProfileSingleProcessPrivilege
SE_PROF_SINGLE_PROCESS_NAME
Profile single process WinNT.h
SeRemoteShutdownPrivilege
SE_REMOTE_SHUTDOWN_NAME
Force shutdown from a remote system WinNT.h
SeRestorePrivilege
SE_RESTORE_NAME
Restore files and directories WinNT.h
SeSecurityPrivilege
SE_SECURITY_NAME
Manage auditing and security log WinNT.h
SeShutdownPrivilege
SE_SHUTDOWN_NAME
Shut down the system WinNT.h
SeSyncAgentPrivilege
SE_SYNC_AGENT_NAME
Synchronize directory service data WinNT.h
SeSystemEnvironmentPrivilege
SE_SYSTEM_ENVIRONMENT_NAME
Modify firmware environment values WinNT.h
SeSystemProfilePrivilege
SE_SYSTEM_PROFILE_NAME
Profile system performance WinNT.h
SeSystemtimePrivilege
SE_SYSTEMTIME_NAME
Change the system time WinNT.h
SeTakeOwnershipPrivilege
SE_TAKE_OWNERSHIP_NAME
Take ownership of files or other objects WinNT.h
SeTcbPrivilege
SE_TCB_NAME
Act as part of the operating system WinNT.h
SeUndockPrivilege
SE_UNDOCK_NAME
Remove computer from docking station WinNT.h
SeUnsolicitedInputPrivilege
SE_UNSOLICITED_INPUT_NAME
Receive unsolicited device input WinNT.h
SeBatchLogonRight
SE_BATCH_LOGON_NAME
Log on as a batch job NTSecAPI.h
SeDenyBatchLogonRight
SE_DENY_BATCH_LOGON_NAME
Deny logon as a batch job NTSecAPI.h
SeDenyInteractiveLogonRight
SE_DENY_INTERACTIVE_LOGON_NAME
Deny logon locally NTSecAPI.h
SeDenyNetworkLogonRight
SE_DENY_NETWORK_LOGON_NAME
Deny access to this computer from the network NTSecAPI.h
SeDenyServiceLogonRight
SE_DENY_SERVICE_LOGON_NAME
Deny logon as a service NTSecAPI.h
SeInteractiveLogonRight
SE_INTERACTIVE_LOGON_NAME
Log on locally NTSecAPI.h
SeNetworkLogonRight
SE_NETWORK_LOGON_NAME
Access this computer from the network NTSecAPI.h
SeServiceLogonRight
SE_SERVICE_LOGON_NAME
Log on as a service NTSecAPI.h

The system actually distinguishes between the privilege and its close cousin, the account right, but they are assigned and revoked identically. In Table 9-10, the privileges are defined in WinNT.h and the account rights are defined in NTSecAPI.h.

Now that you have some familiarity with what privileges are and how the system represents them, let's dive into assigning and revoking privileges on a system.

The LSA Functions

To assign privileges programmatically, you need to become familiar with a set of functions known as the LSA functions. LSA stands for Local Security Authority, which handles user logon and authentication on the local machine. The LSA functions can be used to do a number of tasks, of which we will only be concerned with privilege assignment in this chapter.The first step in using the LSA functions is to retrieve a handle to a policy object. A policy object represents the system on which you will be performing account management functions. To obtain a handle to a policy object, you must call LsaOpenPolicy.

 NTSTATUS LsaOpenPolicy(    PLSA_UNICODE_STRING    plsastrSystemName,    PLSA_OBJECT_ATTRIBUTES pObjectAttributes,    ACCESS_MASK            DesiredAccess,    PLSA_HANDLE            pPolicyHandle); 

This function might look a bit strange at first, because the data types aren't commonly used in other Win32 functions. So let's examine them one by one.

The plsastrSystemName parameter is a pointer to an LSA_UNICODE_STRING structure, which is defined as follows:

 typedef struct _LSA_UNICODE_STRING {    USHORT Length;    USHORT MaximumLength;    PWSTR  Buffer; } LSA_UNICODE_STRING 

Like the Net functions, the LSA functions deal only with strings in Unicode format. However, unlike the Net functions, the LSA functions require that all strings be managed in terms of the LSA_UNICODE_STRING structure, which is simply a string length, a buffer length, and a pointer to a buffer.

NOTE
It is important to remember that the Length (indicating the length of the string) and MaximumLength (indicating the length of the buffer) members are stored in terms of bytes, not characters.

The string pointed to by the Buffer member of the LSA_UNICODE_STRING structure is not explicitly defined to be zero-terminated. Although you can choose to end a string with a zero, you must be sure not to include the null termination in your calculation of the length of the string. Also keep in mind that when the system returns an LSA_UNICODE_STRING structure to your software, it cannot count on zero termination of the string pointed to by the Buffer member.

Dealing with the LSA_UNICODE_STRING type can be a little awkward because it is length-delimited rather than zero-terminated. Because of this, I think that the LSA_UNICODE_STRING virtually begs to be wrapped in a simple C++ class. I've made a class called CLSAStr, which does this, and use it liberally in the TrusteeMan sample application described later in this chapter.

To call LsaOpenPolicy, initialize an instance of the LSA_UNICODE_STRING structure and point it to a Unicode string containing the name of the system for which you wish to manipulate privileges. Passing NULL as the plsastrSystemName parameter indicates the local machine.

The pObjectAttributes parameter points to an instance of the LSA_OBJECT_ATTRIBUTES structure, which is unused at this time and should have each of its members initialized to 0.

The DesiredAccess parameter is declared as type ACCESS_MASK, which maps to a 32-bit unsigned integer. If you will be viewing privilege information on your system, combine POLICY_VIEW_LOCAL_INFORMATION and POLICY_LOOKUP_NAMES as in the example that follows this discussion. If you will be setting privilege information, you should also combine the POLICY_CREATE_ACCOUNT access flag.

The final parameter of LsaOpenPolicy requires that you pass the address of a variable of type LSA_HANDLE. Upon success, the system will place the handle to the open LSA policy object in this variable. Once you have finished with the policy object, you should pass this handle to the LsaClose function:

 NTSTATUS LsaClose(LSA_HANDLE hObjectHandle); 

All LSA functions return a value of type NTSTATUS, and you should pass this value to LsaNtStatusToWinError:

 ULONG LsaNtStatusToWinError(NTSTATUS Status); 

The LsaNtStatusToWinError function converts the NT status code to a familiar Win32 error code just like those returned from GetLastError. After conversion, a successful call to LsaOpenPolicy returns a value of ERROR_SUCCESS.

The following code shows the proper way to open an LSA policy object for a system on the network whose name is "JasonsComputer":

 LSA_OBJECT_ATTRIBUTES   lsaOA = { 0 }; LSA_UNICODE_STRING      lsastrComputer = { 0 }; LSA_HANDLE              hPolicy = NULL; // Computer Name WCHAR* pstrName = L"JasonsComputer"; // Set the size of the useless LSA_OBJECT_ATTRIBUTES structure lsaOA.Length = sizeof(lsaOA); // Fill in the members of the LSA_UNICODE_STRING structure lsastrComputer.Length = (USHORT) (lstrlen(pstrName) * sizeof(WCHAR)); lsastrComputer.MaximumLength = lsastrComputer.Length + sizeof(WCHAR); lsastrComputer.Buffer = pstrName; // Retrieve the policy handle NTSTATUS ntStatus = LsaOpenPolicy(&lsastrComputer, &lsaOA,    POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES |    POLICY_CREATE_ACCOUNT, &hPolicy); ULONG lErr = LsaNtStatusToWinError(ntStatus); if (lErr != ERROR_SUCCESS){    // Handle error } 

Now that you know how to obtain an open handle to an LSA policy object, you are ready to begin managing the privileges on the system.

Enumerating Privileges

There are two ways to enumerate the privileges on a system: either obtain a list of the privileges held by a specific trustee, or request a list of trustees that hold a specific privilege.

Privileges Held by a Specific Trustee

Let's start by discussing how to obtain a list of privileges held by a specific trustee of the system. To enumerate the privileges, use the LsaEnumerateAccountRights function:

 NTSTATUS LsaEnumerateAccountRights(    LSA_HANDLE           hPolicy,     PSID                 psidTrustee,     PLSA_UNICODE_STRING* pplsastrUserRights,      PULONG               plCountOfRights); 

You must pass an open policy object created with POLICY_LOOKUP_NAMES access as the hPolicy parameter of LSAEnumerateAccountRights. The psidTrustee parameter is a pointer to the SID for the trustee for which you want to enumerate privileges. You can use LookupAccountName (discussed earlier in the section "Understanding SIDs") to obtain a SID from a trustee's account name. This trustee can be a user account, group account, or computer account.

You should pass the address of a variable of type LSA_UNICODE_STRING as the pplsastrUserRights parameter of LsaEnumerateAccountRights. The system generates an array of LSA_UNICODE_STRING structures, allocates a buffer to hold the array, and then places a pointer to the buffer in the variable pointed to by the pplsastrUserRights parameter. The system then returns the number of privileges in the array in the ULONG variable pointed to by plCountOfRights.

Because the system has allocated a buffer on your behalf, you must free the buffer when you are finished with it. Passing a pointer to the buffer to the LsaFreeMemory function does this:

 NTSTATUS LsaFreeMemory(PVOID pvBuffer); 

If LsaEnumerateAccountRights succeeds, the translated status code will be ERROR_SUCCESS. If the account has no privileges assigned to it, the translated error code will be ERROR_FILE_NOT_FOUND. The elements of the array returned by LsaEnumerateAccountRights are of type LSA_UNICODE_STRING (defined earlier in the section "The LSA Functions"). Each element will point to a buffer containing a Unicode string representation of an account right, including values such as SeDebugPrivilege and SeEnableDelegationPrivilege. You can refer back to Table 9-10 for a list of all possible returned privileges.

Although it is not directly related to enumerating privileges, the LookupPrivilegeDisplayName function should be mentioned. This function translates a programmatic privilege name, such as SeTcbPrivilege, into its friendly display name, which in this case would be "Act as part of the operating system".

 BOOL LookupPrivilegeDisplayName(    PCTSTR pszSystemName,    PCTSTR pszName,    PTSTR  pszDisplayName,    PDWORD cbDisplayName,     PDWORD pLanguageId); 

This function takes the system name and the programmatic name for the privilege, and then returns the friendly name in a buffer that you supply.

NOTE
LookupPrivilegeDisplayName will not return the display name for an account right. It works only for privileges. Although our discussion here does not distinguish between these two types of account right, this topic is discussed more fully in Chapter 11.

You can determine whether an account right is a privilege by referring to Table 9-10. If the right in question is defined in the WinNT.h header file, it is a privilege. If it is defined in the NTSecAPI.h header file, it is only an account right, not an actual privilege.

The following sample function shows how to list all the privileges held by a trustee. It takes an LSA policy handle and a PSID as its parameters.

 BOOL PrintTrusteePrivs(LSA_HANDLE hPolicy, PSID psid) {    BOOL fSuccess = FALSE;    WCHAR szTempPrivBuf[256];    WCHAR szPrivDispBuf[1024];    PLSA_UNICODE_STRING plsastrPrivs = NULL;    __try {       // Retrieve the array of privileges for the given SID       ULONG lCount = 0;       NTSTATUS ntStatus = LsaEnumerateAccountRights(hPolicy, psid,          &plsastrPrivs, &lCount);       ULONG lErr = LsaNtStatusToWinError(ntStatus);       if (lErr != ERROR_SUCCESS) {          plsastrPrivs = NULL;          __leave;       }       ULONG lDispLen = 0;       ULONG lDispLang = 0;       for (ULONG lIndex = 0; lIndex < lCount; lIndex++) {          // Assure zero termination          lstrcpyn(szTempPrivBuf,             plsastrPrivs[lIndex].Buffer, plsastrPrivs[lIndex].Length);          szTempPrivBuf[plsastrPrivs[lIndex].Length] = 0;          wprintf(L"Programmatic Name: %s\n", szTempPrivBuf);          // Translate to Display Name          lDispLen = 1024; // Size of static Display buffer          if (LookupPrivilegeDisplayName(NULL, szTempPrivBuf,              szPrivDispBuf, &lDispLen, &lDispLang))             wprintf(L"Display Name: %s\n\n", szPrivDispBuf);       }       fSuccess = TRUE;    }    __finally {       if (plsastrPrivs) LsaFreeMemory(plsastrPrivs);    }    return(fSuccess); } 

Trustees That Hold a Specific Privilege

Now for the second way to retrieve privilege information for a Windows 2000 system: request a list of trustees that hold a specific privilege by calling LsaEnumerateAccountsWithUserRight.

 NTSTATUS LsaEnumerateAccountsWithUserRight(    LSA_HANDLE          hPolicy,     PLSA_UNICODE_STRING plsastrUserRight,     PVOID*              ppvEnumerationBuffer,     PULONG              CountReturned); 

This function requires a handle to an LSA policy object much like LsaEnumerateAccountRights does. However, instead of filling an LSA_UNICODE_STRING structure with the name of a trustee, you should pass a pointer to an LSA_UNICODE_STRING structure complete with the programmatic name of a privilege or an account right.

The system returns trustee information to your software by allocating a buffer and returning its pointer via the ppvEnumerationBuffer parameter. Although this parameter is defined as a pointer to a PVOID, you should pass the address of a pointer to a variable of type PLSA_ENUMERATION_INFORMATION because the system will return the information as an array of LSA_ENUMERATION_INFORMATION structures. It would have been nice if the developers of Windows had defined LsaEnumerateAccountsWithUserRight to take a pointer to the correct type, but they chose instead to require a pointer to a PVOID, so you will have to cast this parameter.

The LSA_ENUMERATION_INFORMATION structure is actually very simple and contains only a single member—a pointer to a SID:

 typedef struct _LSA_ENUMERATION_INFORMATION {    PSID Sid; } LSA_ENUMERATION_INFORMATION; 

The number of elements in the array is returned in the variable pointed to by CountReturned, which is the final parameter of LsaEnumerateAccountsWithUserRight. The SIDs in the returned array can be used to generate a trustee name by passing the SID to LookupAccountSid, discussed earlier in this chapter. When you are finished with the buffer returned by LsaEnumerateAccountsWithUserRight, you should pass its pointer to LsaFreeMemory.

Assigning and Removing Privileges

It is uncommon to have to create software solely to enumerate the privileges given to a trustee account. However, just about any software that creates trustee accounts will need to assign (and perhaps remove) privileges from an account. Fortunately, the LSA functions provide two simple functions to perform these tasks. The first, LsaAddAccountRights, is used to grant privileges to a trustee:

 NTSTATUS LsaAddAccountRights(    LSA_HANDLE          hPolicy,    PSID                psidTrustee,    PLSA_UNICODE_STRING plsastrUserRights,    ULONG               lCountOfRights); 

This function is easy to use. You must pass the handle to an open LSA policy object, as well as a pointer to the SID for the trustee whose privileges you wish to modify. (You can obtain a SID for a user or group account by calling LookupAccountName, as discussed earlier in this chapter.)

Before calling LsaAddAccountRights, however, you should build an array of LSA_UNICODE_STRING structures, each with the name of a privilege you want the trustee to hold. Pass a pointer to this array as the plsastrUserRights parameter of LsaAddAccountRights. Finally, pass the number of elements in the array as the final parameter, lCountOfRights.

The LsaAddAccountRights function ignores any account rights or privileges already held by the trustee. It fails, however, if any of the rights in the array are not valid names for the system.

The following code provides an example of how to assign the SeInteractiveLogonRight account right and the SeTcbPrivilege privilege to a trustee account whose SID is pointed to by the variable psid.

 LSA_UNICODE_STRING lsastrPrivs[2] = { 0 }; lsastrPrivs[0].Buffer = SE_INTERACTIVE_LOGON_NAME; lsastrPrivs[0].Length =     lstrlen(lsastrPrivs[0].Buffer) * sizeof(WCHAR); lsastrPrivs[0].MaximumLength = lsastrPrivs[0].Length + sizeof(WCHAR); lsastrPrivs[1].Buffer = SE_TCB_NAME; lsastrPrivs[1].Length =    lstrlen(lsastrPrivs[1].Buffer) * sizeof(WCHAR); lsastrPrivs[1].MaximumLength = lsastrPrivs[1].Length + sizeof(WCHAR); NTSTATUS ntStatus = LsaAddAccountRights(hPolicy, psid,    lsastrPrivs, 2); ULONG lErr = LsaNtStatusToWinError(ntStatus); 

The function used to remove privileges from a trustee account is similar to LsaAddAccountRights. It is called LsaRemoveAccountRights and is prototyped as follows:

 NTSTATUS LsaRemoveAccountRights(    LSA_HANDLE          hPolicy,    PSID                psidTrustee,    BOOLEAN             fAllRights,    PLSA_UNICODE_STRING plsastrUserRights,    ULONG               lCountOfRights); 

This function is the same as LsaAddAccountRights except that it removes the list of rights from the trustee indicated by psidTrustee and has one extra parameter, the Boolean fAllRights.

The fAllRights parameter allows you to remove all privileges from a trustee without building a list of the privileges held by the trustee. If you pass TRUE for this parameter, the plsastrUserRights and lCountOfRights parameters should be NULL and 0, respectively. If you pass FALSE for the fAllRights parameter, LsRemoveAccountRights is used the same way as LsaAddAccountRights.

Before wrapping up our discussion of LsaRemoveAccountRights, I should point out one detail about removing all rights from an account. The Platform SDK documentation states that if you pass TRUE for the fAllRights parameter, the system will remove all privileges from the account and then delete the account from the system. However, this is true only from a certain perspective. Each system running Windows 2000 must maintain a local database of account privilege entries (technically implemented internally as a local account) even if the trustee account lives on another machine such as a domain controller. The LsaRemoveAccountRights function will not delete a trustee account (even if the account is local) from the system that hosts the account; it deletes only the account privilege entries from its local database.



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