Working with the DACL


Most developers are familiar with using security to grant or deny permission to perform a task or use a resource. However, you can use the DACL to perform other tasks as well. The example in this section discusses one of the more important tasks you can perform—verifying a permission’s state. Many Win32 API functions won’t work if the application doesn’t have the correct permission. For example, you can’t easily set data in the SACL without the SeSecurityPrivilege in all cases and the SeAuditPrivilege in some cases. You can find a list of these privileges (28 in all) in the WinNT.H file (located in the \Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Include folder of your hard drive). Listing 15.1 shows how to work with the DACL directly to obtain privilege information for the current process. This listing is incomplete—it doesn’t include the Win32 API function declarations and some of the error handling found in the complete program. (You can find the complete source code for this example in the \Chapter 15\C#\GetTokenData and \Chapter 15\VB\GetTokenData folders of the source code located on the Sybex Web site.)

Listing 15.1 Obtaining Permission Information

start example
private void btnTest_Click(object sender, System.EventArgs e) {    Int32             LastError;          // Last Win32 API error.    IntPtr            ProcessHandle;      // Current process.    IntPtr            ProcessTokenHandle; // Process access token.    IntPtr            TokenPriv;          // Token privileges pointer.    Int32             TokenPrivLen;       // Length of the token data.    Int32             RequiredLen;        // Required structure length.    TOKEN_PRIVILEGES  TokenStruct;        // Access token data.    StringBuilder     Output;             // Output information.    // Open the process token.    ProcessHandle = GetCurrentProcess();    if (!OpenProcessToken(ProcessHandle,       TokenAccessFlags.TOKEN_ADJUST_PRIVILEGES |       TokenAccessFlags.TOKEN_QUERY,       out ProcessTokenHandle))    {       // Get the last error.       LastError = Marshal.GetLastWin32Error();       // Display an error message and exit if not successful.       MessageBox.Show("Couldn’t obtain process token handle." +                       "\r\nLast Error: " + LastError.ToString(),                       "Application Error",                       MessageBoxButtons.OK,                       MessageBoxIcon.Error);            // Close the process handle and return.       CloseHandle(ProcessHandle);       return;    }    // Determine the length of the data field.    TokenPriv = new IntPtr(0);    GetTokenInformation(ProcessTokenHandle,                        TOKEN_INFORMATION_CLASS.TokenPrivileges,                        TokenPriv,                        0,                        out RequiredLen);    // Get the access token information.    TokenPriv = Marshal.AllocHGlobal(RequiredLen);    TokenPrivLen = RequiredLen;    GetTokenInformation(ProcessTokenHandle,                        TOKEN_INFORMATION_CLASS.TokenPrivileges,                        TokenPriv,                        TokenPrivLen,                        out RequiredLen);    // Convert the data to a usable form.    TokenStruct = new TOKEN_PRIVILEGES();    TokenStruct = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(       TokenPriv, typeof(TOKEN_PRIVILEGES));    // Free the memory.    Marshal.FreeHGlobal(TokenPriv);    // Close the handles.    CloseHandle(ProcessTokenHandle);    CloseHandle(ProcessHandle);    // Process the information...      // Display the information on screen... }
end example

It doesn’t take long to figure out that the Win32 API uses handles for everything. A handle allows you to access the object in question. Consequently, the first task the code performs for this program is to locate the handle for the current process using the GetCurrentProcess() function. Once the code has a handle to the current process, it uses the handle to get a process token handle using the OpenProcessToken() function. Notice that you must tell Windows how you intend to use the process token handle—the application asks permission to adjust the privileges and query the token for information. You must prepare for failure when you work with these functions because neither the Win32 API nor CLR will handle the errors for you. A function could fail and you wouldn’t know about it until you tried to use the result as input to another function. The example shows one of the many forms of error handling used by the Win32 API.

Many of the Win32 API functions use variable length data. When working with variable length data, you normally need to make two calls. The first call tells you how much memory to allocate to retrieve the data, while the second call obtains the data. The example uses this two call procedure with the GetTokenInformation() function. Notice that in the first call, the fourth argument is 0—this number indicates that the code doesn’t know how much memory to allocate. The RequiredLen argument contains the length of the required buffer on return from the call. In this case, the code asks for the token privileges for the current process by using the ProcessTokenHandle value and the TOKEN_INFORMATION_CLASS.TokenPrivileges enumeration member. Also notice that the code allocates memory from the global heap using the Marshal.AllocHGlobal() method. This method allocates unmanaged memory for use with Win32 API calls. Because CLR doesn’t know anything about unmanaged memory, you must make all the calls.

The pointer you receive from the GetTokenInformation() function isn’t very useful because you can’t access the data it points at. The next step is to create a managed structure in which to place the data. You use the Marshal.PtrToStructure() method to move the data pointed to in unmanaged memory by TokenPriv to the managed TokenStruct data structure. This process can fail in a number of ways, none of which appears in the documentation. The most common problem is making the data structure too complex by including arrays or other indeterminate features. The code will compile, but you’ll see any number of odd runtime errors that seem to have nothing to do with the actual problem. The best practice is to make the data structure as simple as possible—a difficult task given the complexities of Win32 API data structures.

At this point, you have the data you need, so there’s no point in keeping all that unmanaged memory in use. The code uses the Marshal.FreeHGlobal() method to free the unmanaged memory allocated from the heap. Likewise, the code uses the CloseHandle() function to free both the handles it used to obtain access to the process access token. The code goes on to create an output string and display it on screen. See the source code for the details of this process. Figure 15.1 displays the results of this example on my machine—your machine will likely differ.

click to expand
Figure 15.1: Windows NT–based systems can support up to 28 privileges.

Although the WinNT.H file lists 28 possible privileges, you’ll seldom find that a machine has them all turned on. When you run into a problem getting an application to work, make sure you have all the proper permissions turned on and enabled. The code presented in this example could reside in a debug library file that you automatically add to all of your programs to make this check. Notice that although my machine has the SeAuditPrivilege turned on, it doesn’t have the privilege enabled. The “Working with the SACL” section discusses why this fact is important.




.Net Development Security Solutions
.NET Development Security Solutions
ISBN: 0782142664
EAN: 2147483647
Year: 2003
Pages: 168

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