Chapter 15. Securing Windows Objects

Windows supports a comprehensive security model that prevents unauthorized access to objects such as files, processes, and file mappings. Nearly all sharable objects can be protected, and the programmer has a fine granularity of control over access rights. Windows has Common Criteria Certification at Evaluation Assurance Level 4 (EAL-4), an internationally recognized criteria.

Security is a large subject that cannot be covered completely in a single chapter. Therefore, this chapter concentrates on the immediate problem of showing how to use the Windows security API to protect objects from unauthorized access. While access control is only a subset of Windows security functionality, it is of direct concern to those who need to add security features to their programs. The initial example, Program 15-1, shows how to emulate UNIX file permissions with NT file system (NTFS) files, and a second example applies security to named pipes. The same principles can then be used to secure other objects. The bibliography lists several resources you can consult for additional security information.

Program 15-1 chmodW: Change File Permissions

image

image

Security Attributes

This chapter explores Windows access control by proceeding from the top down to show how to construct an object’s security. Following an overview, the Windows functions are described in detail before proceeding to the examples. In the case of files, it is also possible to use Windows Explorer to examine and manage some file security attributes.

Nearly any object created with a Create system call has a security attributes parameter. Therefore, programs can secure files, processes, threads, events, semaphores, named pipes, and so on. The first step is to include a SECURITY_ATTRIBUTES structure in the Create call. Until now, our programs have always used a NULL pointer in Create calls or have used SECURITY_ATTRIBUTES simply to create inheritable handles (Chapter 6). In order to implement security, the important element in the SECURITY_ATTRIBUTES structure is lpSecurityDescriptor, the pointer to a security descriptor, which describes the object’s owner and determines which users are allowed or denied various rights.

An individual process is identified by its access token, which specifies the owning user and group membership. When a process attempts to access an object, the Windows kernel can determine the process’s identity using the token and can then decide from the information in the security descriptor whether or not the process has the required rights to access the object.

Chapter 6 introduced the SECURITY_ATTRIBUTES structure; for review, here is the complete structure definition:

Set nLength to sizeof(SECURITY_ATTRIBUTES). bInheritHandle indicates whether or not the handle is inheritable by other processes.

Security Overview: The Security Descriptor

Analyzing the security descriptor gives a good overview of essential Windows security elements. This section mentions the various elements and the names of the functions that manage them, starting with security descriptor structure.

A security descriptor is initialized with the function InitializeSecurityDescriptor, and it contains the following:

• The owner security identifier (SID) (described in the next section, which deals with the object’s owner)

• The group SID

• A discretionary access control list (DACL)—a list of entries explicitly granting and denying access rights. The term “ACL” without the “D” prefix will refer to DACLs in our discussion.

• A system ACL (SACL), sometimes called an “audit access ACL,” controls audit message generation when programs access securable objects; you need to have system administrator rights to set the SACL.

SetSecurityDescriptorOwner and SetSecurityDescriptorGroup associate SIDs with security descriptors, as described in the upcoming “Security Identifiers” section.

ACLs are initialized using the InitializeAcl function and are then associated with a security descriptor using SetSecurityDescriptorDacl or SetSecurityDescriptorSacl.

Figure 15-1 shows the security descriptor and its components.

Figure 15-1 Constructing a Security Descriptor

image

Access Control Lists

Each ACL is a set (list) of access control entries (ACEs). There are two types of ACEs: one for access allowed and one for access denied.

You first initialize an ACL with InitializeAcl and then add ACEs. Each ACE contains a SID and an access mask, which specifies rights to be granted or denied to the user or group specified by the SID. FILE_GENERIC_READ and DELETE are typical file access rights.

The two functions used to add ACEs to discretionary ACLs are AddAccessAllowedAce and AddAccessDeniedAce. AddAuditAccessAce is for adding to an SACL. Finally, remove ACEs with DeleteAce and retrieve them with GetAce.

Using Windows Object Security

There are numerous details to fill in, but Figure 15-1 shows the basic structure. Notice that each process also has SIDs (in an access token), which the kernel uses to determine whether access is allowed. The user’s access token may also give the owner certain privileges (the ability to perform system operations such as system shutdown and to access system resources). These user and group privileges are set when the administrator creates the account.

The kernel scans the ACL for access rights for the user, based on the user’s ID and group. The first entry that specifically grants or denies the requested service is decisive. The order in which ACEs are entered into an ACL is therefore important. Frequently, access-denied ACEs come first so that a user who is specifically denied access will not gain access by virtue of membership in a group that does have such access. In Program 15-1, however, it is essential to mix allowed and denied ACEs to obtain the desired semantics.

Object Rights and Object Access

An object, such as a file, gets its security descriptor at creation time, although the program can change the security descriptor at a later time.

A process requests access to the object when it asks for a handle using, for example, a call to CreateFile. The handle request contains the desired access, such as FILE_GENERIC_READ, in one of the parameters. If the security descriptor grants access to the process, the request succeeds. Different handles to the same object may have different access rights. The access flag values are the same for both allowing and denying rights when creating ACLs.

Security Descriptor Initialization

The first step is to initialize the security descriptor using the InitializeSecurityDescriptor function. Set the pSecurityDescriptor parameter to the address of a valid SECURITY_DESCRIPTOR structure. These structures are opaque and are managed with specific functions.

Security descriptors are classified as either absolute or self-relative. This distinction is ignored for now but is explained near the end of the chapter.

dwRevision is set to the constant SECURITY_DESCRIPTOR_REVISION.

Security Descriptor Control Flags

Flags within the Control structure of the security descriptor, the SECURITY_DESCRIPTOR_CONTROL flags, control the meaning assigned to the security descriptor. Several of these flags are set or reset by the upcoming functions and will be mentioned as needed. GetSecurityDescriptorControl and SetSecurityDescriptorControl access these flags, but the examples do not use the flags directly.

Security Identifiers

Windows uses SIDs to identify users and groups. The program can look up a SID from the account name, which can be a user, group, domain, and so on. The account can be on a remote system. The first step is to determine the SID from an account name.

Parameters

lpSystemName and lpAccountName point to the system and account names. Frequently, lpSystemName is NULL to indicate the local system.

Sid is the returned information, which is of size *cbSid. The function will fail, returning the required size, if the buffer is not large enough.

ReferencedDomainName is a string of length *cbReferencedDomainName characters. The length parameter should be initialized to the buffer size (use the usual techniques to process failures). The return value shows the domain where the name is found. The account name Administrators will return BUILTIN, whereas a user account name will return that same user name.

peUse points to a SID_NAME_USE (enumerated type) variable and can be tested for values such as SidTypeWellKnownGroup, SidTypeUser, SidTypeGroup, and so on.

Getting the Account and User Names

Given a SID, you reverse the process and obtain the account name using LookupAccountSid. Specify the SID and get the name in return. The account name can be any name available to the process. Some names, such as Everyone, are well known.

Obtain the process’s user account name (the logged-in user) with the GetUserName function.

The user name and length are returned in the conventional manner.

Create and manage SIDs using functions such as InitializeSid and AllocateAndInitializeSid. The examples confine themselves, however, to SIDs obtained from account names.

Once SIDs are known, they can be entered into an initialized security descriptor.

pSecurityDescriptor points to the appropriate security descriptor, and pOwner (or pGroup) is the address of the owner’s (group’s) SID. As always in such situations, assure that these SIDs were not prematurely freed.

bOwnerDefaulted (or bGroupDefaulted) indicates, if TRUE, that a default mechanism is used to derive the owner (or primary group) information. The SE_OWNER_DEFAULTED and SE_GROUP_DEFAULTED flags within the SECURITY_DESCRIPTOR_CONTROL structure are set according to these two parameters.

The similar functions GetSecurityDescriptorOwner and GetSecurityDescriptorGroup return the SID (either owner or group) from a security descriptor.

Managing ACLs

This section shows how to manage ACLs, how to associate an ACL with a security descriptor, and how to add ACEs. Figure 15-1 shows the relationships between these objects and functions.

The first step is to initialize an ACL structure. The ACL should not be accessed directly, so its internal structure is not relevant. The program must, however, provide a buffer to serve as the ACL; the functions manage the contents.

pAcl is the address of a programmer-supplied buffer of cbAcl bytes. Subsequent discussion and Program 15-4 will show how to determine the ACL size, but 1KB is more than adequate for most purposes. dwAclRevision should be ACL_REVISION.

Next, add the ACEs in the order desired with the AddAccessAllowedAce and AddAccessDeniedAce functions.

pAcl points to the same ACL structure initialized with InitializeAcl, and dwAclRevision is ACL_REVISION. pSid points to a SID, such as one that would be obtained from LookupAccountName.

The access mask (dwAccessMask) determines the rights to be granted or denied to the user or group specified by the SID. The predefined mask values will vary by the object type.

The final step is to associate an ACL with the security descriptor. In the case of the discretionary ACL, use the SetSecurityDescriptorDacl function.

bDaclPresent, if TRUE, indicates that there is an ACL in the pAcl structure. If FALSE, pAcl and fDaclDefaulted, the next two parameters, are ignored. The SECURITY_DESCRIPTOR_CONTROL’s SE_DACL_PRESENT flag is also set to this parameter’s value.

The final flag is fDaclDefaulted. FALSE indicates an ACL generated by the programmer. TRUE indicates that the ACL was obtained by a default mechanism, such as inheritance. The SE_DACL_DEFAULTED flag in the SECURITY_DESCRIPTOR_CONTROL is set to this parameter value.

Other functions delete ACEs and read ACEs from an ACL; we discuss them later in this chapter. It is now time for an example.

Example: UNIX-Style Permission for NTFS Files

UNIX file permissions provide a convenient way to illustrate Windows security, even though Windows security is much more general than standard UNIX security.

First, however, here is a very quick review of UNIX file permissions (directories are treated slightly differently).

• Every file has an owning user and group.

• Every file has 9 permission bits, which are specified as 3 octal (base 8) digits.

• The 3 bits in each octal digit grant, or deny, read (high-order bit), write, and execute (low-order bit) permission. Read, write, and execute permissions are displayed as r, w, and x respectively. Execute rights are meaningful for .exe and .bat files but not for .txt files.

• The 3 octal digits, from left to right, represent rights given to the owner, the group, and to everyone else.

• Thus, if you set the permissions to 640, the permissions will be displayed as rw_r_____. The file owner can read and write the file, group members can read it, and everyone else has no access.

The implementation creates nine ACEs to grant or deny read, write, and execute permissions to the owner, group, and everyone. There are two commands.

1. chmodW sets the permissions and is modeled after the UNIX chmod command. The implementation has been enhanced to create the specified file if it does not already exist and to allow the user to specify the group name.

2. lsFP displays the permissions along with other file information and is an extension of the lsW command (Program 3-2). When the long listing is requested, the command displays the owning user and an interpretation of the existing ACLs, which may have been set by chmodW.

Programs 15-1 and 15-2 show the implementation for these two commands. Programs 15-3, 15-4, and 15-5 show three supporting functions:

1. InitializeUnixSA, which creates a valid security attributes structure corresponding to a set of UNIX permissions. This function is general enough that it can be used with objects other than files, such as processes (Chapter 6), named pipes (Chapter 11), and synchronization objects (Chapter 8).

2. ReadFilePermissions.

3. ChangeFilePermissions.

Program 15-2 lsFP: List File Permissions

image

Program 15-3 InitUnSA: Initializing Security Attributes

image

image

image

image

Program 15-4 ReadFilePermissions: Reading Security Attributes

image

image

Program 15-5 ChangeFilePermissions: Changing Security Attributes

image

Note: The separate DeniedAceMasks array assures that SYNCHRONIZE rights are never denied because the SYNCHRONIZE flag is set in all three of the macros, FILE_GENERIC_READ, FILE_GENERIC_WRITE, and FILE_GENERIC_EXECUTE, which are combinations of several flags (see the include file, winnt.h). The full program in the Examples file provides additional explanation.

The programs that follow are simplifications of the programs from the Examples file. For example, the full program checks to see if there is a group name on the command line; here, the name is assumed. Also, there are command line flags to create a file that does not exist and to suppress the warning message if the change fails.

Program 15-2 shows the relevant part of lsFP—namely, the ProcessItem function. Other parts of the program are similar to Chapter 3’s lsW Program 3-2.

The next step is to show the supporting function implementations. However, Run 15-2 first shows the two new commands in operation. First, a new file is created, and its permissions are seen to be 700 (the owner can read, write, and execute the file; others have no rights). Next, the owner’s write permission is removed, and an attempt to write to the file is denied. Once the write permissions are restored, the file write succeeds, and the file listing (cat command) shows that the new text is at the end of the file. Finally, others are given read permission.

Run 15-2 chmodW, lsFP: UNIX-like File Permissions

image

Example: Initializing Security Attributes

Program 15-3 shows the utility function InitializeUnixSA, which creates a security attributes structure containing an ACL with ACEs that emulate UNIX file permissions. There are nine ACEs granting or denying read, write, and execute permissions for the owner, the group, and everyone else. The actual array of three rights (read, write, and execute for files) can vary according to the object type being secured. This structure is not a local variable in the function but must be allocated and initialized and then returned to the calling program; notice the ACE mask arrays in Program 15-1.

Two aspects of this program are interesting and could be modified (see the exercises).

• The function creates a heap and allocates memory from the heap. This greatly simplifies destroying the SA (DestroyUnixSA is at the end). The heap is returned from the function; alternatively, you could create an opaque structure containing the heap and the SA.

• The SDs in the SA are “absolute” rather than self-relative; a later section talks about this some more.

Comments on Program 15-3

Program 15-3 may have a straightforward structure, but its operation is hardly simple. Furthermore, it illustrates several points about Windows security that need review.

• Several memory allocations are required to hold information such as the SIDs. They are created in a dedicated heap, which the calling program eventually destroys. The advantage is that it’s simple to free the allocated memory (there are seven allocations) with a single HeapDestroy call.

• The security attribute structure in this example is for files, but it is also used with other objects such as named pipes (Chapter 11). Program 15-4 shows how to integrate the security attributes with a file.

• To emulate UNIX behavior, the ACE entry order is critical. Notice that access-denied and access-allowed ACEs are added to the ACL as the permission bits are processed from left (Owner/Read) to right (Everyone/Execute). In this way, permission bits of, say, 460 (in octal) will deny write access to the user even though the user may be in the group.

• The ACEs’ rights are access values, such as FILE_GENERIC_READ and FILE_GENERIC_WRITE, which are similar to the flags used with CreateFile. The calling program (Program 15-1 in this case) specifies the rights that are appropriate for the object.

• The defined constant ACL_SIZE is large enough to contain the nine ACEs. After Program 15-5, it will be apparent how to determine the required size.

• The function uses three SIDs, one each for User, Group, and Everyone. Three different techniques are employed to get the name to use as an argument to LookupAccountName. The user name comes from GetUserName, or get the user SID from the current token without getting the user name (also avoiding the problem of getting an impersonating name; this is Exercise 15-5). The name for everyone is Everyone in a SidTypeWellKnownGroup. The group name is a command line argument and is looked up as a SidTypeGroup. Finding the groups that the current user belongs to requires some knowledge of process token, and solving this problem is Exercise 15–12. Incidentally, finding the groups of an arbitrary user is fairly complex.

• The version of the program in the Examples file, but not the one shown here, is fastidious about error checking. It even goes to the effort to validate the generated structures using the self-explanatory IsValidSecurityDescriptor, IsValidSid, and IsValidAcl functions. This error testing proved to be helpful during debugging.

Reading and Changing Security Descriptors

Now that a security descriptor is associated with a file, the next step is to determine the security of an existing file and, in turn, change it. The following functions get and set file security in terms of security descriptors.

secInfo is an enumerated type that takes on values such as OWNER_SECURITY_INFORMATION, GROUP_SECURITY_INFORMATION, DACL_SECURITY_INFORMATION, and SACL_SECURITY_INFORMATION to indicate what part of the security descriptor to get or set. Combine these values with the bit-wise “or” operator.

To figure out the size of the return buffer for GetFileSecurity, the best strategy is to call the function twice. The first call simply uses 0 as the cbSd value. After allocating a buffer, call the function a second time. Program 15-4 operates this way.

Needless to say, the correct file permissions are required in order to carry out these operations. For example, it is necessary to have WRITE_DAC permission or to be the object’s owner to succeed with SetFileSecurity.

The functions GetSecurityDescriptorOwner and GetSecurityDescriptorGroup can extract the SIDs from the security descriptor obtained with GetFileSecurity. Obtain the ACL with the GetSecurityDescriptorDacl function.

The parameters are nearly identical to those of SetSecurityDescriptorDacl except that the flags are returned to indicate whether a discretionary ACL is actually present and was set as a default or by a user.

To interpret an ACL, first find out how many ACEs it contains.

In most cases, the ACL information class, dwAclInfoClass, is AclSizeInformation, and the pAclInformation parameter is a structure of type ACL_SIZE_INFORMATION. AclRevisionInformation is the other value for the class.

An ACL_SIZE_INFORMATION structure has three members: the most important one is AceCount, which shows how many entries are in the list. To determine whether the ACL is large enough, look at the AclBytesInUse and AclBytesFree members of the ACL_SIZE_INFORMATION structure.

The GetAce function retrieves ACEs by index.

Obtain the ACEs (the total number is now known) by using an index. pAce points to an ACE structure, which has a member called Header, which, in turn, has an AceType member. Test the ACE type for ACCESS_ALLOWED_ACE and ACCESS_DENIED_ACE.

Example: Reading File Permissions

Program 15-4 is the function ReadFilePermissions, which Programs 15-1 and 15-2 use. This program methodically uses the preceding functions to extract the information. Its correct operation depends on the fact that the ACL was created by Program 15-3. The function is in the same source module as Program 15-3, so the definitions are not repeated.

Example: Changing File Permissions

Program 15-5 completes the collection of file security functions. This function, ChangeFilePermissions, replaces the existing security descriptor with a new one, preserving the user and group SIDs but creating a new discretionary ACL.

Securing Kernel and Communication Objects

The preceding sections were concerned mostly with file security, and the same techniques apply to other filelike objects, such as named pipes (Chapter 11), and to kernel objects. Program 15-6, the next example, deals with named pipes, which can be treated in much the same way as files.

Program 15-6 ServerNP: Securing a Named Pipe

image

Securing Named Pipes

While the code is omitted in the Program 11-3 listing, the server (whose full code appears in the Examples file) optionally secures its named pipe to prevent access by unauthorized clients. Optional command line parameters specify the user and group name.

Server [UserName GroupName]

If the user and group names are omitted, default security is used. Note that the full version of Program 11-3 (in the Examples file) and Program 15-6 use techniques from Program 15-3 to create the optional security attributes. However, rather than calling InitUnixSA, we now use a simpler function, InitializeAccessOnlySA, which only creates access-allowed ACEs. Program 15-6 shows the relevant code sections that were not shown in Program 11-3. The important security rights for named pipes are follows:

FILE_GENERIC_READ

FILE_GENERIC_WRITE

These two values provide SYCHRONIZE rights. The server in Program 15-6 optionally secures its named pipe instances using these rights. Only clients executed by the owner have access, although it would be straightforward to allow group members to access the pipe as well.

Kernel and Private Object Security

Many objects, such as processes, threads, and mutexes, are kernel objects. To get and set kernel security descriptors, use GetKernelObjectSecurity and SetKernelObjectSecurity, which are similar to the file security functions described in this chapter. However, you need to know the access rights appropriate to an object; the next subsection shows how to find the rights.

It is also possible to associate security descriptors with private, programmer-generated objects, such as a proprietary database. The appropriate functions are GetPrivateObjectSecurity and SetPrivateObjectSecurity. The programmer must take responsibility for enforcing access and must provide security descriptors with calls to CreatePrivateObjectSecurity and DestroyPrivateObjectSecurity.

ACE Mask Values

The “user, group, everyone” model that InitUnixSA implements will be adequate in many cases, although different models are possible using the same basic techniques.

It is necessary, however, to determine the actual ACE mask values appropriate for a particular kernel object. The values are not always well documented, but there are several ways to determine the values for different kernel objects.

• Read the documentation for the open call for the object in question. The access flags are the same as the flags in the ACE mask. For example, OpenMutex uses MUTEX_ALL_ACCESS and SYNCHRONIZE (the second flag is required for any object that can be used with WaitForSingleObject or WaitForMultipleObjects). Other objects, such as processes, have many additional access flags.

• The “create” documentation may also supply useful information.

• Inspect the header files winnt.h and winbase.h for flags that apply to the object.

Example: Securing a Process and Its Threads

The OpenProcess documentation shows a fine-grained collection of access rights, which is appropriate considering the various functions that can be performed on a process handle. For example, PROCESS_TERMINATE access is required on a process handle in order for a process (actually, a thread within that process) to terminate the process that the handle represents. PROCESS_QUERY_INFORMATION access is required in order to perform GetExitCodeProcess or GetPriorityClass on a process handle. PROCESS_ALL_ACCESS permits all access, and SYNCHRONIZE access is required to perform a wait function.

To illustrate these concepts, JobShellSecure.c upgrades Chapter 6’s JobShell job management program so that only the owner (or administrator) can access the managed processes. The program is in the Examples file.

Overview of Additional Security Features

There is much more to Windows security, but this chapter is an introduction, showing how to secure Windows objects using the security API. The following sections give a brief overview of additional security subjects that some readers will want to explore.

Removing ACEs

The DeleteAce function deletes an ACE specified by an index, in a manner similar to that used with GetAce.

Absolute and Self-Relative Security Descriptors

Program 15-5, which changed ACLs, had the benefit of simply replacing one security descriptor (SD) with another. To change an existing SD, however, some care is required because of the distinction between absolute (ASD) and self-relative SDs (SRSD). The internal details of these data structures are not important for our purposes, but it is important to understand why there are two distinct SD types and how to convert between them.

• During construction, an SD is absolute, with pointers to various structures in memory. InitializeSecurityDescriptor creates an absolute SD. An absolute SD cannot be associated with a permanent object, such as a file, because the structure refers to memory addresses. However, an absolute SD is easy to modify and is fine for a process, thread, event, or other object that is not persistent and is represented by in-memory data structures.

• When the SD is associated with a permanent object, Windows consolidates the SD into a compact “self-relative” structure (SRSD) that can be associated with the object in the file system.

• An SRSD is more compact and more appropriate to pass as a function argument, but it is difficult to change.

• It is possible to convert between the two forms using Windows functions for that purpose. Use MakeAbsoluteSD to convert an SRSD, such as the one returned by GetFileSecurity. Modify the ASD and then use MakeSelfRelativeSD to convert it back. MakeAbsoluteSD is one of the more formidable Windows functions, having 11 parameters: two for each of the four SD components, one each for the input and output SDs, and one for the length of the resulting absolute SD.

InitializeUnixSA constructs a SA containing multiple ASDs. Exercise 15–16 suggests using only SRSDs.

System ACLs

There is a complete set of functions for managing system ACLs; only system administrators can use it. System ACLs specify which object accesses should be logged. The principal function is AddAuditAccessAce, which is similar to AddAccessAllowedAce. There is no concept of access denied with system ACLs.

Two other system ACL functions are GetSecurityDescriptorSacl and SetSecurityDescriptorSacl. These functions are comparable to their discretionary ACL counterparts, GetSecurityDescriptorDacl and SetSecurityDescriptorDacl.

Access Token Information

Program 15-1 did not solve the problem of obtaining the groups associated with a process in its access token. Program 15-1 simply required the user to specify the group name. You use the GetTokenInformation function for this, providing a process handle (Chapter 6). Exercise 15–12 addresses this issue, providing a hint toward the solution. The solution code is also included in the Examples file.

SID Management

The examples obtained SIDs from user and group names, but you can also create new SIDs with the AllocateAndInitializeSid function. Other functions obtain SID information, and you can even copy (CopySid) and compare (CompareSid) SIDs.

Summary

Windows implements an extensive security model that goes beyond the one offered by standard UNIX. Programs can secure all objects, not just files. The example programs have shown how to emulate the UNIX permissions and ownership that are set with the umask, chmod, and chown functions. Programs can also set the owner (group and user). The emulation is not easy, but the functionality is much more powerful. The complexity reflects the complexity of the requirements.

Looking Ahead

This chapter completes our presentation of the Windows API.

Additional Reading

Windows

Microsoft Windows Security Resource Kit, Second Edition, by Smith, Komar, and the Microsoft Security Team, and Microsoft Windows Server 2003 PKI and Certificate Security, by Brian Komar, provide in depth coverage.

Windows Design and Architecture

Windows Internals: Including Windows Server 2008 and Windows Vista, Fifth Edition, by Solomon, Russinovich, and Ionescu, describes details of Windows security internal implementation.

Common Criteria

See www.commoncriteriaportal.org/thecc.html for information about the Common Criteria levels and the Common Criteria Recognition Agreement.

Exercises

15–1. Extend Program 15-1 so that multiple groups have their own unique permissions. The group name and permission pairs can be separate arguments to the function.

15–2. Extend Program 15-4 so that it can report on all the groups that have ACEs in the object’s security descriptor.

15–3. Confirm that chmodW has the desired effect of limiting file access.

15–4. Investigate the default security attributes you get with a file.

15–5. What are some of the other access masks you can use with an ACE? The Microsoft documentation supplies some information.

15–6. Enhance both chmodW and lsFP so that they produce an error message if asked to deal with a file on a non-NTFS file system. GetVolumeInformation is required.

15–7. Enhance the chmodW command so that there is an -o option to set the owning user to be the user of the chmodW program.

15–8. Determine the actual size of the ACL buffer that Program 15-3 needs to store the ACEs. Program 15-3 uses 1,024 bytes. Can you determine a formula for estimating the required ACL size?

15–9. The Cygwin Web site (www.cygwin.com) provides an excellent open source Linux-like environment on Windows with a shell and implementations of commands including chmod and ls. Install this environment and compare the implementations of these two commands with the ones developed here. For example, if you set file permissions using the Cygwin command, does lsFP properly show the permissions, and conversely? Compare the Cygwin source code with this chapter’s examples to contrast the two approaches to using Windows security.

15–10. The compatibility library contains functions _open and _unmask, which manage file permissions. Investigate their emulation of UNIX file permissions and compare it with the solutions in this chapter.

15–11. Write a command, whoami, that will display your logged-in user name.

15–12. Program 15-3, which created a security descriptor, required the programmer to supply the group name. Modify the program so that it creates permissions for all the user’s groups. Hint: Use the OpenProcessToken function, which returns an array with the group names, although you will need to experiment to find out how the array stores group names. The source program in the Examples file contains a partial solution.

15–13. Note in the client/server system that the clients can access exactly the same files and other objects that are available to the server on the server’s machine with the server’s access rights. Remove this limitation by implementing security delegation using the functions ImpersonateNamedPipeClient and RevertToSelf. Clients that are not in the group used to secure the pipe cannot connect to the server.

15–14. There are several additional Windows functions that you may find useful and that could be applied to simplify or improve this chapter’s examples. Look up the following functions: AreAllAccessesGranted, AreAnyAccessesGranted, AccessCheck, and MapGenericMask. Can you use these functions to simplify or improve the examples?

15–15. chmodW (Program 15-1) calls GetUserName, and a code comment suggests an alternative, getting the SID from the current token, to avoid an impersonation problem. Implement and test that change.

15–16. InitializeUnixSA uses a heap to simplify destroying the SA structure. An alternative, and arguably superior, method would be to convert all the SDs to be self-relative, use normal (malloc) allocations, and use free. However, if InitializeUnixSA fails before completing, be sure to free the memory that has been allocated.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.97.222