Working with ACEs Example

So far, we've looked at examples of how to work with the access token and the security descriptor and the vagaries of working with specific objects such as files. This example completes the tour of security support for the Win32 API by looking at the ACEs that make up the SACL and the DACL. Because you're most likely to work with the DACL, this example emphasizes access over auditing. However, working with the ACEs in either structure is about the same. Listing 8.3 shows how you'd access the ACEs for a file. The listing is incomplete—it doesn't include the functions used in previous examples. Make sure you check the source code in the \Chapter 08\C#\GetGroupAccess and \Chapter 08\VB\GetGroupAccess folders of the CD for details. This source code includes an encapsulated version of the code used to gain access to the security descriptor in the form of the GetFileSD() function.

Listing 8.3: Gaining Access to the ACEs Means Reading the ACL

public const Int32 ERROR_SUCCESS = 0;

// This function uses the DACL to retrieve an array of explicit // entries, each of which contains information about individual ACEs // within the DACL.

[DllImport("AdvAPI3 2.DLL", CharSet=CharSet.Auto, SetLastError=true )] public static extern Int32 GetExplicitEntriesFromAcl( IntPtr pacl, ref UInt32 pcCountOfExplicitEntries, out EXPLICIT_ACCESS []pListOfExplicitEntries);

// This data structure is used to create the explicit entry array. [StructLayout(LayoutKind.Sequential, Pack=1)]

public struct EXPLICIT_ACCESS {

public UInt32 grfAccessPermissions;

public ACCESS_MODE grfAccessMode; public UInt32 grfInheritance;

public TRUSTEE Trustee;

// The ACCESS_MODE enumeration tells what type of ACE entry we're // working with.

public enum ACCESS_MODE {

NOT_USED_ACCESS = 0,

GRANT_ACCESS,

SET_ACCESS,

DENY_ACCESS,

REVOKE_ACCESS,

SET_AUDIT_SUCCESS,

SET_AUDIT_FAILURE

// This structure contains the trustee information for the ACE. [StructLayout(LayoutKind.Sequential, Pack=1)]

public struct TRUSTEE {

public IntPtr pMultipleTrustee;

public MULTIPLE_TRUSTEE_OPERATION MultipleTrusteeOperation; public TRUSTEE_FORM TrusteeForm;

public TRUSTEE_TYPE TrusteeType;

public String ptstrName;

// The MULTIPLE_TRUSTEE_OPERATION enumeration determines if this // is a single or a multiple trustee.

public enum MULTIPLE_TRUSTEE_OPERATION {

NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_IMPERSONATE,

// The TRUSTEE_FORM enumeration determines what form the ACE trustee // takes.

public enum TRUSTEE_FORM {

TRUSTEE_IS_SID,

TRUSTEE_IS_NAME,

TRUSTEE_BAD_FORM,

TRUSTEE_IS_OBJECTS_AND_SID,

TRUSTEE_IS_OBJECTS_AND_NAME

// The TRUSTEE_TYPE enumeration determines the type of the trustee.

public enum TRUSTEE_TYPE {

TRUSTEE_IS_UNKNOWN,

TRUSTEE_IS_USER,

TRUSTEE_IS_GROUP,

TRUSTEE_IS_DOMAIN,

TRUSTEE_IS_ALIAS,

TRUSTEE_IS_WELL_KNOWN_GROUP,

TRUSTEE_IS_DELETED,

TRUSTEE_IS_INVALID,

TRUSTEE_IS_COMPUTER

// This function retrieves the DACL from the file's security // descriptor.

[DllImport("AdvAPI3 2.DLL", CharSet=CharSet.Auto, SetLastError=true )] public static extern bool GetSecurityDescriptorDacl( IntPtr pSecurityDescriptor, ref Boolean lpbDaclPresent, out IntPtr pDacl, ref Boolean lpbDaclDefaulted);

private void btnTest_Click(object sender, System.EventArgs {

Boolean

Boolean

IntPtr

Int32

UInt32

EXPLICIT ACCESS

DACLPresent; // Is the DACL present?

Defaulted; // Is the DACL defaulted?

ACECount; // Number of ACEs in DACL.

// Obtain a security descriptor containing the DACL. if (!GetFileSD(txtFile.Text,

SECURITY_INFORMATION.DACL_SECURITY_INFORMATION)) return;

DACLPresent = false;

Defaulted = false;

if (!GetSecurityDescriptorDacl(SecurityDescriptor, ref DACLPresent, out DACL, ref Defaulted))

// Display an error message.

MessageBox.Show("Unable to retrieve the DACL.", "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

// Free the memory we allocated.

Marshal.FreeHGlobal(SecurityDescriptor);

return;

is a DACL to display.

// If not, tell the user there is no DACL. MessageBox.Show("There is no DACL.", "Processing Report", MessageBoxButtons.OK, MessageBoxIcon.Information);

// Free the memory we allocated. Marshal.FreeHGlobal(SecurityDescriptor);

return;

// Obtain the array of ACEs from the DACL. ACECount = 0;

Result = GetExplicitEntriesFromAcl(DACL, ref ACECount, out ACEList);

// Check the results.

// Display an error message.

MessageBox.Show("Unable to retrieve the ACEs.", "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

// Free the memory we allocated. Marshal.FreeHGlobal(SecurityDescriptor);

return;

// Display the number of ACEs.

MessageBox.Show("The file has " + ACECount.ToString() + " ACEs attached to it.", "Number of ACEs", MessageBoxButtons.OK, MessageBoxIcon.Information);

// Free the memory we allocated. Marshal.FreeHGlobal(SecurityDescriptor);

The code begins with a simple define—a reminder that the various Win32 API functions return different values. In this case, the GetExplicitEntriesFromAcl() function returns a value of ERROR_SUCCESS if successful or an error value if unsuccessful. You compare the return value with constants to determine the cause of error.

Notice that the GetExplicitEntriesFromAcl() function is also unique in that it's the only function so far that requires an array as input. You don't define a specific number of array elements—just the fact that the return value is an array. The call will still work, in this case, whether you provide an IntPtr or a single EXPLICIT_ACCESS structure value. The difference is that you won't actually be able to use the return value if you don't use an array.

Warning Microsoft acknowledges problems with the various functions used to work with ACEs. For example, the GetExplicitEntriesFromAclO function can return the incorrect number of ACEs in some cases.

(See Microsoft Knowledge Base Article Q260307 for details.) The suggested alternatives of working with the GetAclInformation(), GetAce(), and LookupAccountSid() functions doesn't really replace the missing functionality, so you might need to get creative at times in using the Win32 API. Make sure you check for appropriate Microsoft Knowledge Base articles at http://search.support.microsoft_.com/search/default.aspx when you run into problems with any of the Win32 API _functions.

The EXPLICIT_ACCESS structure is relatively complex. It includes both an enumerated value and another structure, TRUSTEE. The other two values are flags, which means you have to go through the complicated comparison routine we've used in other examples to determine what the flag values mean.

While the TRUSTEE structure looks relatively simple, it can become complex because it also includes enumerated values that determine what each of the fields in the structure means. For example, the ptstrName variable has meaning only if the TRUSTEE_FORM enumeration value is TRUSTEE_IS_NAME. Matters are further complicated by hidden rules. The MULTIPLE_TRUSTEE_OPERATION should always equal NO_MULTIPLE_TRUSTEE because Microsoft hasn't implemented this feature yet, or at least its developers haven't documented it.

The GetSecurityDescriptorDacl() is another of the functions we talked about earlier for working with the security descriptor. Remember that you should never change the security descriptor directly because other applications might try to access it at the same time. This function has an odd return value until you consider that most parts of the security descriptor are optional. The lpbDaclPresent tells you if the DACL is present in the security descriptor. The call can succeed even if the security descriptor doesn't contain a DACL, so you need to know this additional information.

In general, the btnTest_Click() method doesn't contain too many surprises. Of course, the first major call is to GetSecurityDescriptorDacl() because the code has to check the security descriptor created with the GetFileSD() function for a DACL. If there's no DACL, the application hasn't actually experienced an error—it's simply found an unprotected file. Consequently, you need to handle the return as a type of legitimate return value. It simply might not be the return value you were expecting.

The next call is to GetExplicitEntriesFromAcl(). Theoretically, the ACECount variable could contain a 0 on return, so you should check it. Again, it's not an actual application error—the DACL could simply be empty. It's unlikely that you'll ever see this happen unless the GetExplicitEntriesFromAcl() function experiences some type of error (see the previous warning for details).

At this point, we're ready to test the code. Figure 8.8 shows that the example file contains four ACE entries. When you run the code, you'll find that it reports the same number.

Temp.txt Properties fen«« Secji, Simtmr foot» 01 umt rwr«i

Temp.txt Properties fen«« Secji, Simtmr foot» 01 umt rwr«i

C .'jfwi IMAiN'Jnfr!

{J SYSTEM

fj l»tlMAIH>JJt«ll

c

Ml ] | Rwiov« ]

Pwretucm lot Adwtiii tfoi i

PwdlEMO«

Wit*

Spco*4 Peinrnnxii

FiJCortM Modfy

PwdlEMO«

Wit*

Spco*4 Peinrnnxii

Fc< H<0<l P<HWtl<M W to XO^QI. I ¿it+jMvmil cidiAiMncfld I

J [ Cencd ngure 8.8: The example application will output the number of ACEs associated with the test file, as shown here.

Was this article helpful?

0 0

Post a comment