Chapter 24. UAC Security

The previous chapters have dealt with general Visual Basic programming tasks. They show how to write the Visual Basic code needed to build an application.

This chapter discusses User Account Control (UAC) security issues. UAC is a system implemented by recent versions of Windows operating systems that allows programs to elevate their privileges only when they absolutely must.

In earlier operating systems that don't have UAC, users often logged in with administrator privileges to perform fairly routine tasks because the programs they used might need administrator privileges. Now, with UAC, users can run with normal user privileges and only elevate their privileges to perform the specific tasks that need them.

UAC OVERVIEW

In general, a program cannot perform actions that require privileges that the user doesn't have. If the user doesn't have permission to delete files in the Windows directory, a program that the user can run should not be able to delete those files either. Otherwise, the user could perform actions that are supposed to be prohibited.

Developers have long known that an application should require the fewest privileges possible to get its job done. If a program needs a lot of privileges, only the users who have those privileges can use it.

Unfortunately, many applications occasionally perform some fairly powerful operations. They may sometimes need to create or delete a file in the Windows directory, access system-related parts of the Registry, or modify environment settings. If the program needs those privileges, the users running the program must have those privileges. That means that many users run programs while logged in as a system administrator so that they have the necessary permissions.

Carrying around all of those permissions comes with some additional risk. If the program misbehaves, it could wreak havoc on the operating system. Even if the program itself works properly, the user might accidentally do something disastrous while logged on as an administrator. An inadvertent keystroke or mouse click could delete important files or drag them into oblivion, making it difficult to restore the system.

A better solution would be to allow a program to temporarily increase its privileges while it performs these powerful operations. If the program made a mistake while running some other part of its code, it would not have enough privileges to do serious harm. The user would not need to constantly have administrative privileges, so system-destroying accidents would be much less likely.

This chapter describes some of the new tools that you can use to minimize the user's exposure to administrator privileges. It explains how to write applications that normally run with normal user privileges, but can use more powerful administrative privileges when necessary.

In older versions of the Windows operating system, when you logged in, the system gave you an access token that later determined the kinds of operations you were allowed to perform. If you logged in as an administrator, your token would let you do just about everything.

The Windows 7 operating system's UAC system takes a slightly different approach. Now, when you log in as an administrator, the system creates two tokens. The first token has only standard user privileges, and the second has full administrative privileges. You begin running using the first token, and the second is saved in case it is needed later.

When you try to perform a task that requires extra privileges, UAC displays a dialog box asking for your approval. If you approve the action, your privileges are elevated to the full administrator token until you finish the action. Then your privileges return to the normal user-level token.

If you are a logged in as a normal user without administrative privileges, you may still be able to perform administrative tasks. When you try to execute a command that requires elevated privileges, UAC presents a dialog box warning you and allowing you to log in as an administrator. If you log in successfully, you are granted administrator privileges until you finish the action.

The difference between these two scenarios is small. If you are logged in as an administrator, UAC only asks you to confirm that you want elevated privileges. If you are logged in as another user, UAC requires you to enter an administrator's password.

DESIGNING FOR UAC

UAC will not elevate an application's privileges after it is running. UAC assigns privileges when the application starts, and will not change the privileges after that. If an application needs to run with elevated privileges, it must obtain those privileges when it starts.

To avoid giving your application more privileges than necessary, you should separate your code into the pieces that require elevated privileges and those that do not. The main program should run with normal user privileges. Later, it should execute other applications that run with elevated privileges when necessary.

For example, a program that saves data into its local database or into a SQL Server database doesn't need administrator privileges. However, the Windows directory is protected, so a program that creates a summary file in that directory needs administrator privileges. You could separate this program into a piece that performs most of the work and another program that writes the summary information into the log file. Before closing, the first program would run the second to write into the file.

If possible, it's better to rewrite an application slightly to avoid requiring special privileges. For example, consider that many applications are installed in the Program Files directory. That directory is protected, so an application requires special privileges to write into it. That means if the application saves information into a file located where its executable is, it will need extra privileges. You can work around this problem by making the program write to a file located in the current user's directory hierarchy.

Other operations that require elevated privileges include writing into other protected directories, interacting directly with hardware, and modifying protected sections of the Registry such as HKEY_LOCAL_MACHINE.

Breaking an application into privileged and non-privileged parts not only lets the main program run with the fewest privileges possible, but separating the application into high- and low-privileges sections will probably help you reduce the number of places that you need extra privileges. That should simplify the riskiest parts of your code and make them easier to debug. It will also improve the separation between the two pieces, making them even easier to debug.

Example programs ShellUAC and ExecuteMe, which are available for download on the book's web site, demonstrate this handoff. Program ShellUAC uses the following code to run the ExecuteMe program, which is flagged for execution with elevated privileges. (How to flag an application in this way is described in the following sections.)

Private Sub btnRun_Click() Handles btnRun.Click
    Try
        ' Start the process.
        Dim pro As System.Diagnostics.Process
        pro = System.Diagnostics.Process.Start(
            txtProgram.Text, txtArguments.Text)

        ' Wait for the process to exit.
        pro.WaitForExit()

        ' Display the process's exit code.
        MessageBox.Show("Exit code: " & pro.ExitCode)

    Catch ex As System.ComponentModel.Win32Exception
        ' This happens if the user fails to elevate to Administrator.
    MessageBox.Show("Operation canceled",
            "Canceled", MessageBoxButtons.OK,
            MessageBoxIcon.Information)
    End Try
End Sub

                                                  
DESIGNING FOR UAC

The code uses the System.Diagnostics.Process.Start function to execute the application. It passes the function the path to the program to execute, and the command-line parameters for the program that are entered in text boxes.

The code calls the returned object's WaitForExit method, so it waits until the other program has finished. It then checks the process's ExitCode property to see what value the ExecuteMe application returned.

The following code shows the ExecuteMe program's Main subroutine:

Function Main(ByVal cmdArgs() As String) As Integer
    Dim frm As New frmChoices
    ' Display the arguments.
    For Each str As String In cmdArgs
        frm.lstArguments.Items.Add(str)
    Next str

    ' Select the first item.
    If frm.lstArguments.Items.Count > 0 Then
        frm.lstArguments.SelectedIndex = 0
    End If

    ' Return the index of the selected item.
    If frm.ShowDialog() = DialogResult.Cancel Then
        Return −1
    Else
        Return frm.lstArguments.SelectedIndex
    End If
End function


                                                  
DESIGNING FOR UAC

The program starts by creating a frmChoices form and adding its command-line arguments to the form's lstArguments list box. It selects the first item in the list and displays the form modally.

If the user clicks the form's Cancel button, the program returns −1. If the user clicks OK, the program returns the index of the selected list item. The Process object in calling program ShellUAC receives the return value in its ExitCode parameter.

As part of the UAC user experience, any action that requires privilege elevation (and, therefore, a UAC dialog box) should be marked with the standard UAC shield. Figure 24-1 shows the ShellUAC example program displaying a button with this shield. The button displays the UAC shield to warn the user that clicking it launches an application that requires privilege elevation.

Buttons that launch actions that require privilege elevation should display the UAC shield.

Figure 24.1. Buttons that launch actions that require privilege elevation should display the UAC shield.

Unfortunately, there currently isn't a really simple way to display the UAC shield in Visual Basic applications. However, you can use an API call to make a button display the shield. The ShellUAC program uses the AddShieldToButton subroutine shown in the following code to make a button display the shield:

Imports System.Runtime.InteropServices

Module UacStuff
    Declare Auto Function SendMessage Lib "user32.dll" _
        (ByVal hWnd As HandleRef, ByVal msg As Int32, _
        ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Int32
    ' Make the button display the UAC shield.
    Public Sub AddShieldToButton(ByVal btn As Button)
        Const BCM_SETSHIELD As Int32 = &H160C

        btn.FlatStyle = Windows.Forms.FlatStyle.System
        SendMessage(New HandleRef(btn, btn.Handle),
            BCM_SETSHIELD, IntPtr.Zero, CType(1, IntPtr))
    End Sub
End Module


                                                  
Buttons that launch actions that require privilege elevation should display the UAC shield.

The module declares the SendMessage API function. Subroutine AddShieldToButton sets the button's FlatStyle property to System, and then uses SendMessage to send the button the BCM_SHIELD message. Example programs RunStartAs and ShellUAC, which are available for download on the book's web site, demonstrate this code to add the UAC shield to their buttons.

Microsoft provides no method for adding the UAC shield to controls other than buttons. For example, if you want to add a shield to a menu item or link label, you're on your own. You could make a shield image and simply place it on your controls, but the image would not change when the system's version of the image changes. If the user changes the system's font size, the standard shield may grow smaller or larger on the system.

Example program AddShields, which is also available for download on the book's web site, works around this problem. It uses the previously described method to add a UAC shield to a button. It then makes the button draw itself onto a bitmap and extracts the shield graphic from the bitmap. You Can see the result in Figure 24-2. Download the example to see the details.

The AddShields program adds UAC shields to Button, PictureBox, and menu item.

Figure 24.2. The AddShields program adds UAC shields to Button, PictureBox, and menu item.

ELEVATING PROGRAMS

The following sections describe three methods for running a program with elevated privileges. These methods differ in who decides that the called program should be elevated: the user, the calling program, or the called program.

User

To elevate an application by using the Run As command, the user can right-click the executable and select the "Run as administrator" command. The operating system displays the UAC dialog box and, after the user enters an administrator password, the program executes with elevated privileges.

This method is simple and requires no extra action on your part, but it does require the user to perform an extra step, so it is not always the best solution. Nevertheless, if the user runs the program only very rarely, it may make sense to require this extra step. This will discourage the user from using the program too often.

Calling Program

Just as a user can start a program with elevated privileges, another application can start a program with elevated privileges. This technique is similar to the one used by the user: the program executes the program, asking the operating system to run the program as an administrator.

The StartRunAs example program uses the following code to execute another program. This program is intended for use with program NoPrivs, which is also available for download. Program NoPrivs does not request privilege elevation itself so the StartRunAs program uses the following code to do so:

Try
    ' Use the runas verb to start the process.
    Dim psi As New ProcessStartInfo
    psi.Verb = "runas"
    psi.UseShellExecute = True
    psi.FileName = txtProgram.Text
    psi.Arguments = txtArguments.Text

    Dim pro As System.Diagnostics.Process
    pro = System.Diagnostics.Process.Start(psi)

    ' Wait for the process to exit.
    pro.WaitForExit()

    ' Display the process's exit code.
    MessageBox.Show("Exit code: " & pro.ExitCode)

Catch ex As System.ComponentModel.Win32Exception
    ' This happens if the user fails to elevate to Administrator.
MessageBox.Show("Operation canceled",
        "Canceled", MessageBoxButtons.OK,
        MessageBoxIcon.Information)
End Try


                                                  
Calling Program

This code builds a ProcessStartInfo object describing the program that the code needs to start. The code sets the object's Verb property to "runas" to indicate that the program should run as an administrator. The code also sets the name of the program and arguments to pass to it, and then calls Process.Start to start the program. The code then waits for the called program to finish and displays its exit status.

Called Program

If you always know that a program must run with elevated permissions, you can make the program request its own elevation so the user and calling program don't need to do it. This technique uses a manifest embedded within the application to request elevation.

To create the manifest, open Solution Explorer and double-click My Project. On the Application tab, click the View UAC Settings button to open the file app.manifest. The following code shows the initial manifest (with comments slightly reformatted to fit on the page):

<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"
 xmlns:asmv1="urn:schemas-microsoft-com:asm.v1"
 xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
         <-- UAC Manifest Options
           If you want to change the Windows User Account Control level
           replace the requestedExecutionLevel node with one of
           the following.
      <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/ >
      <requestedExecutionLevel level="highestAvailable" uiAccess="false" />

          If you want to utilize File and Registry Virtualization
          for backward compatibility then delete the
          requestedExecutionLevel node.
         -->
        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>
</asmv1:assembly>


                                                  
Called Program

To make the program request UAC elevation, change the uncommented requestedExecutionLevel so the level is requireAdministrator. Now when you compile the program, Visual Studio flags the executable as requiring administrator privilege. When the user or another program executes this program, the system automatically tries to elevate it to administrator privileges and displays the UAC elevation dialog.

SUMMARY

The UAC programming standards require an application to use the fewest privileges necessary to get the job done. An application should run with normal user privileges if possible.

If the application must perform some task that requires greater privileges, it can execute a separate application that has elevated privileges.

This chapter shows three methods for running a program with elevated privileges. First, you can ask the user to right-click the executable program and select "Run as administrator." This is not the most convenient strategy, but should be acceptable for programs that the user only needs to run rarely, or that only rarely need administrator privileges.

Second, you can make the calling application start the new one with elevated privileges. This is more convenient for the user because he or she doesn't need to right-click the program and select "Run as administrator," but this still allows you to run the called program with or without privileges as needed.

Third, you can embed a manifest inside the called application that makes it always request privilege elevation. This is most appropriate when the called program should never run without elevated privileges.

The chapters in the book so far focus on specific Visual Basic programming details that you need to understand to write Visual Basic applications. They explain the Visual Basic development environment, language syntax, standard controls and forms, custom controls, drag and drop, and the clipboard. This chapter describes some UAC security issues that you need to be aware of if you want to perform tasks that require elevated user privileges.

The chapters in the next part of the book deal with higher-level object-oriented programming (OOP) issues. They explain fundamental concepts in object-oriented development and how they apply to Visual Basic. They tell how to build and use classes and objects, and they describe some of the standard classes that Visual Basic and the .NET Framework provide to perform common programming tasks.

Chapter 25 starts by explaining fundamental ideas behind object-oriented programming, such as the three main features of OOP: encapsulation, polymorphism, and inheritance. It explains the benefits of these features and tells how you can take advantage of them in Visual Basic.

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

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