About company Our development services Portfolio. Case studies Support Testimonials Contacts Brutka Ltd: web site development and custom software development

Information technology news archive


Least Privilege: Teach Your Apps To Play Nicely With Windows Vista User Account Control

I
have worked with so many developers over the past year that their concerns and their eventual understanding of how User Account Control (UAC) works have all blended into a giant haze. There really is no doubt: it will take time to learn how to write good applications for standard users, but this knowledge will make you a better programmer for Windows®. The goal of this article is to give you the information necessary to make the journey quick and easy.
UAC is the Microsoft answer to reducing the privileges users run with by default in Windows Vista™. Strategically, Microsoft is moving to an environment where users do not have or need privileges that can affect the operating system and machine-wide configuration in order to perform day-to-day tasks. Such an environment also keeps users from affecting each other's state and settings.
There are countless practical reasons for this move. UAC limits the privileges that applications run with by default. In the enterprise, this lets users run in the Users group rather than as Administrators, allowing meaningful corporate security policies to be enforced. This in turn reduces user impact on machines, lowering the total cost of ownership (TCO). In the home, malware cannot affect the system or install a service or driver without the user going through the UAC Consent or Credential prompt. While the risk of social engineering still exists, the consolidation of the path to obtain elevated privileges allows the future implementation of technologies to control which applications can execute with machine-wide privileges without the need to prompt the user.
The primary goal of UAC is to make the default user token that applications are run with mimic that of a member of the Users group. This starts with creating a restricted, or filtered, token during interactive logon for users with elevated privileges. When more privileges are necessary to execute a restricted operation, the user will be prompted for authorization on the secure desktop. UAC also includes other technologies to support application compatibility and to secure the elevated processes.
Active at Logon
UAC starts working when a user logs onto a machine. During an interactive logon, the Local Security Authority (LSA) takes the user's credentials and performs the initial logon, evaluating the user's token to see if it has what are defined as elevated privileges. If the LSA determines that the user has elevated privileges, it will filter this token and then perform a second logon with the filtered token. The most common case in which a user will have both a filtered and fully privileged token is when the user is a member of the Administrators group. The fully privileged token of an administrator can write to machine-wide locations such as Program Files and HKEY_LOCAL_MACHINE (HKLM), thus affecting the entire machine. However, the filtered token is just like a member of the Users group, which doesn't have these privileges, and is used to create the user's interactive desktop session.
The desktop session and explorer.exe will always be created with a token that approximates the token of a member of the Users group. Any process that is initiated from the Start Menu or by a user double-clicking in an Explorer window that doesn't require elevation will simply inherit this filtered token. Therefore, by default, every application will be running with the standard user token. When a process is marked as requiring administrator privileges in the manifest or in the application compatibility settings, UAC will prompt the user for elevation. Figures 1 and 2 show the prompts for consent and for credentials. If the user so authorizes, the Application Information service will create the process with the fully privileged token.
Creating a Filtered Token
User Account Control defines the groups listed in Figure 3 as having elevated privileges. Therefore, if the LSA notices that any of those group memberships or privileges are listed in the user's initial token, a filtered token will be created during an interactive logon, using a version of the CreateRestrictedToken API, and the fully privileged token is saved by LSA. These two tokens are linked and the fully privileged token can be obtained from the filtered token using the GetTokenInformation API with the new TokenLinkedToken information type. Note, however, that UAC does not affect service, network, or batch logons.
If the user does not belong to any of the groups listed in Figure 3 but has certain privileges, a filtered token will be created with these privileges removed. The privileges in question are: SeCreateTokenPrivilege, SeTcbPrivilege, SeTakeOwnershipPrivilege, SeBackupPrivilege, SeRestorePrivilege, SeDebugPrivilege, SeImpersonatePrivilege, and SeRelabelPrivilege.
If the user is a member of the Administrators group, the filtered token will have the Administrators group membership set to DENY ONLY, which prohibits AccessCheck from using this group to allow access to a resource. Also, all machine-impacting privileges are removed from the token. (This is the default token used to create explorer.exe and non-elevated processes.) You can look at this token using whoami.exe, which is included in Windows Vista; just run whoami.exe /all in a Command window. Figure 4 and Figure 5 respectively describe group memberships listed in the token and privileges in the token.

Designing Apps to Run with the Filtered Token
Most applications do not require administrator privileges at run time. If your application doesn't maintain cross-session state while it executes and doesn't do something like modifying the local security policy, it should be just fine running with a standard-user token. Sometimes certain parts of your application will require administrator privileges, and you should separate out those pieces into a separate process. I'll get into that a little later.
The most important step you can take during the development of a standard user application is to test it while running as a standard user. One of the most common reasons an application fails in production is because the developers never tested it as a standard user. Remember to do this.
The next most important step is deciding where to save your application binaries and per-user configuration data. An application that is completely per-user, meaning that both its binaries and configuration are specific to the user, should maintain all of its state in the user profile: %userprofile%. In the registry, you should write all of the state to the user's hive, HKEY_CURRENT_USER (HKCU). In many cases, it makes sense to install the binaries into the %ProgramFiles% directory but this requires administrator privileges and is discussed later in the article.
Sometimes, as when you're storing high scores for a game, you need to maintain system-wide state and allow users to write to it. The recommended place to store this data is in the %allusersprofile% directory. Here you can create a directory specific to your application and allow users to write to it. Just remember, other users can tamper with this file.

Designing Apps that Require Administrator Privileges
My honest advice about writing applications that require administrator privileges is: don't do it! Unless you're absolutely certain that your application requires elevated privileges, just write your application to use the standard user token. There are very few APIs that must be called by administrators.
Of course, there are circumstances where you might need to use administrator privileges, like during installation or when you're maintaining cross-session state, which generally means your application is implemented as a service. UAC doesn't apply to services; there are other security settings available for Windows services that I'll discuss later. If your application implements a security model on the local machine, you'd also need administrator privileges. For example, if you have settings that are deployed with a Group Policy Application Distribution and Management (ADM) or through Systems Management Server (SMS), you will probably have an application on the local machine to update them as well. This would require administrator privileges.

Privileges During Installation
Installing applications typically requires administrator privileges, usually because application binaries are written to the Program Files directory, which is read-only for standard users. This means that any application that writes binaries to this location will need to be marked as requiring administrator privileges. Because Windows Installer has been integrated with UAC, installation packages (MSI files) have become an even more attractive method for installation. They can be specified in two ways: to be per-user or per-machine installations, which would only prompt the user for administrator privileges when necessary.
While installation to the Program Files directory will result in an elevation prompt being shown to users, there are definitely benefits to putting the application's binaries there. Installing in that directory requires the user to be an administrator so a standard user in an enterprise or a Parental Controls user couldn't arbitrarily install the application. Also, your application will share one set of binaries across the machine and they will be protected from tampering by a standard user. This will make it easier to determine the state and version of the application when updating the binaries after a bug fix or to add functionality.
When you design your application, there will likely be some application settings you'll want applied for the user. Perhaps the users specify custom information about how their views are shown or which menus are displayed. A good way to provide this facility is to create a default template under the Program Files directory, then copy and modify it in the user's AppData directory, %localappdata%. This data can be copied at first run of your application and enables per-user state for each user. If, on the other hand, you want machine-wide state that individual users can modify, you would want to put this in the %allusersprofile%, as in the high scores example mentioned earlier.
Another aspect of installation that causes some grief in the UAC world is updating. If you are going to update binaries in the Program Files directory, the process overwriting the binaries must have administrator or system privileges. One benefit of using Windows Installer is the ability to patch using an MSI patch (MSP) file. Since Windows Installer 3.1, which is a required update for Windows XP, an MSP file will be applied to the system if it is signed by a certificate that was included in the original MSI file. For more information, see the documentation on User Account Control Patching on MSDN® (at windowssdk.msdn.microsoft.com/ms710366.aspx).
Other than using MSI files, application developers can either integrate a binary that is specified to run with administrator privileges-which will prohibit updating in an enterprise with standard users-or include a service that will execute any updates required. Shipping a service is strongly discouraged because any security vulnerability in the service could make the ISV an attack vector for malware. MSP files are your best bet.

Marking the Privileges for an MSI
Before Windows Vista, the ALLUSERS property was used to mark whether an MSI file would install an application's shortcuts to the user location or for all users on the machine. These shortcuts included the DesktopFolder, ProgramMenuFolder, StartMenuFolder, and StartupFolder. Because there was no analogous per-user Program Files directory, the application binaries generally were still written to the Program Files directory.
Unfortunately, for app compatibility reasons Windows Installer could not determine whether the user should be prompted for credentials based solely on the ALLUSERS property. Instead, an additional bit was allocated in the MSI file to determine whether to prompt the user. This is bit 3 in the Word Count Summary property. If this bit is set to 1, the package is assumed to be a per-user MSI and the user will not be prompted for an Administrator token.
To specify that a package can only be installed by an administrator into the Public profile, set ALLUSERS="1" or ALLUSERS="2" and set bit 3 of the Word Count Summary property to 0. To specify that a package is a per-user installation that can be installed by a standard user, set ALLUSERS="" or don't define the property, and set bit 3 of the Word Count Summary property to 1.

Running Your App with Administrator Privileges
There are times that you may need administrator privileges for an application. You may write code that directly interacts with a piece of hardware or an app that sets machine-wide settings in HKLM. When possible, you should design your apps to limit the need for admin privileges to narrow sections of code, or communicate to an application started with full administrator privileges.
Aside from MSI-based elevation, there are two ways to create processes with a user's full administrator token. The Application Information Service (AIS) will check during process creation and during the creation of a COM object using the CoCreateAsAdmin moniker to see if the binaries require administrator privilege. It is important to note that the elevation occurs at the time of process creation. The process token never has privileges or group membership added during run time, only when it is created.
One word of caution: the UAC elevation prompt is only presented to the user when ShellExecute is called to create the process. ERROR_ELEVATION_REQUIRED is returned by any call to the CreateProcess family that requires elevation.

Marking Required Privileges Using an Application Manifest
When a new process is created, the AIS will inspect the binary to determine whether it requires elevation. The first thing that gets checked is the application manifest that is embedded into the application's resources. This takes precedence over any other type of application marking including an application compatibility marking or UAC's Installer Detection, which is described later. The manifest defines a run level that tells Windows the privileges needed to run the binary. The three choices for run level are: asInvoker, highestAvailable, and requireAdministrator.
When AIS finds a binary that is marked with the "asInvoker" run level, it takes no action and the process inherits the process token of the parent process that created it. The "requireAdministrator" run level is pretty straightforward as well and defines that the process must be created by a user token that is a member of the administrator group. If the user who attempted to create this process is not an administrator, he will be presented with the Credential dialog to input his credentials.
The highestAvailable run level is a little more complicated. It denotes that if a user has a linked token, then the application should run with the higher privileged token. This is generally used for applications that have a UI designed for the Users and Administrators groups and it ensures that the application gets the user's full privileges. It is important to note that users in the Backup Operators and Network Operators group will have a linked token and will be prompted for their credentials; the credentials dialog will have their user tile and those of the members of the administrators group.
The application manifest marking is only relevant to EXEs, not DLLs. This is because UAC does not inspect DLLs during the creation of the process. To embed this manifest into your native application, copy the manifest file into the same directory as your source and add the manifest into the resource files to specify that the manifest is to be embedded. The following lines in the .rc file would embed the manifest above if it were saved as AdminApp.exe.manifest:
#define MANIFEST_RESOURCE_ID 1
MANIFEST_RESOURCE_ID RT_MANIFEST "AdminApp.exe.manifest"

Marking a COM Object
When a COM object is being created, you can use the CoCreateAsAdmin moniker to get a COM object running with elevated privileges. This is very useful for creating objects that run elevated and are exposed in your application's UI.
There are a couple additions that must be made to a COM object so it can run with the fully privileged token. A display name to show in the UAC elevation dialog must be specified as well as a marking showing that the object can be created with elevated privileges. The following registry keys must be added to the registration of your object.
HKEY_LOCAL_MACHINE\Software\Classes\CLSID\
{CLSID}\LocalizedString = <displayname>
HKEY_LOCAL_MACHINE\Software\Classes\CLSID\
{CLSID}\Elevation\Enabled = 1
If the COM object is not registered in HKLM and both of these pieces of information are not specified, then trying to create the elevated COM object will fail. If it succeeds, the user will be prompted for elevation, assuming the call is coming from a process with a filtered token, and a new process will be created to host the COM object. For more information about creating elevated COM objects, see the MSDN documentation for the CoCreateAsAdmin moniker at msdn2.microsoft.com/ms679687.aspx.

Alerting Users to Elevated Privileges
Another challenge for UAC is communicating to users that may not have any notion of the difference between an application that is running with reduced privileges and one that is running as an administrator. The key to understanding this difference is the standardized workflow when the prompt for elevation occurs. The elevation sequence always begins with the user clicking on something that has a shield image in its icon. If starting an application is going to cause an elevation, you should see a shield overlaying the icon. Anywhere in your application that causes elevation should be marked with a shield.
Many of the common controls have been updated to allow you to add shields to them. This is extremely helpful if you want to expose settings where clicking on a button or link results in the creation of a new process that will require elevation. The code to add the shield glyph is very simple. For example, the standard button control (PUSHBUTTON, DEFPUSHBUTTON) has been enhanced to let you add an icon along with the displayed text, without requiring the BS_ICON or BS_BITMAP styles to be set.
To show the shield icon, you should call the following macro (defined in commctrl.h), which sends a SETSHIELD message to the button:
Button_SetElevationRequiredState(hwndButton, true);
The same style of showing the shield can be used for Aero™ Wizard buttons, Syslink and Hyperlink controls, Commandlink and Command buttons, and more.

Communicating between Processes in Different Security Contexts
Windows Vista also includes an authentication technology called Windows Integrity Mechanism (WIM). WIM defines different security levels that are used as labels on objects for further authorization: Low, Medium, High, and System. Because of UAC there may be processes in the same desktop session that have full administrator privileges and others that are running with a standard user token. UAC uses WIM to block Windows messages from being sent between processes of different privilege levels.
The token used to create the desktop will be running at the medium integrity level-see the sample filtered administrator token. This way everything by default is running with an integrity level of medium. When a user is prompted for elevation and gives his consent, the resulting process is created with the fully privileged token and has an integrity level of high. In the past, there have been attacks, called "shatter attacks," where a lower-privileged UI sends messages to a higher-privileged UI to drive the higher privileged process. In order to avoid this, most Windows messages are no longer sent from lower privileged processes to higher privileged processes, and this is based on the integrity level.
There are still ways to communicate between processes of different integrity levels. You can use shared memory or a remote procedure call (RPC). Be sure that when you create your RPC entry point or IPC mechanism, you specify that it can be connected to by a process that is running as a standard user. You will need to specify this in the object's security descriptor.

Low Rights Internet Explorer
One other side note about WIM and UAC is that it enables LowRights Internet Explorer®, a setting that takes advantage of the low integrity level to protect users while they browse the Web. When running at Low, the process can interact with a very narrow set of directories and files on the system. If you have an ActiveX® control implemented as part of your application, which is hosted by Internet Explorer, you will want to test it to make sure it runs properly. For more information, see the whitepaper at msdn.microsoft.com/library/en-us/ietechcol/dnwebgen/protectedmode.asp.

Securing a Windows Service in Windows Vista
There are many new facilities for locking down services in Windows Vista. Developers can specify the privilege level that a service needs, and a service can have a Security Identifier (SID) applied to it, which can be used to secure resources so that only the service can write to them. Services can also be prevented from accessing the network. For more information, see the documentation on Windows services available at microsoft.com/whdc/system/vista/Vista_Services.mspx.
You can still use sc.exe to define these new security parameters in Windows Vista. Privileges is a string that contains a list of privileges that are separated by a forward slash (/). For example, to specify backup and restore privileges, set Privileges to SeBackupPrivilege/SeRestorePrivilege.
Sc.exe can also be used to get a specified service's SID. The following command returns the SID for a service that is named service name.
Sc showsid [service name]
To programmatically change this information, use the new API called ChangeServiceConfig2. To change the service privileges specified, call ChangeServiceConfig2 with the following parameter values. (The change in privileges takes effect the next time the service is started.)
The dwInfo parameter should be SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO and the lpInfo buffer should point to a SERVICE_REQUIRED_PRIVILEGES_INFO structure. This structure contains a single multistring that lists the required privileges.
To enforce the service SID for the service, set the flag programmatically, calling ChangeServiceConfig2 with the following parameter values. (The change takes effect the next time the system is booted.) The dwInfo parameter should be set to SERVICE_CONFIG_SERVICE_SID_INFO, and the lpInfo buffer should be set to point to a SERVICE_SID_INFO structure. This structure contains a single DWORD member that contains the SID type.
Two related public functions are also useful to service owners: LookupAccountName and LookupAccountSID. LookupAccountName takes a SID and returns the associated service name while LookupAccountSID takes a service name and returns the associated SID. To query the values of these settings use the QueryServiceConfig2 API.
Another way to protect your service is to limit its access to the network. To configure firewall restrictions for the service, use the INetFwServiceRestriction interface.
One last note-be wary of exposing RPC interfaces that can be connected to from lower-privileged processes. These interfaces should be carefully designed and tested so that the tasks that can be performed on the user's behalf do not allow an elevation of privilege by the user.

Common Application Compatibility Issues
One of the biggest challenges our team encountered while producing UAC was the incredible impact it had on our application ecosystem. Many applications were not designed to be run by a member of the Users group. Microsoft has invested many hours to understand the common application compatibility issues and has added technologies to Windows Vista to address some of the problems. In the final section of this article, I will describe some of the problems and briefly describe how to fix them.
The first application compatibility technology that is part of UAC is called Installer Detection. Because most installers write binaries to the Program Files directory, they overwhelmingly need administrator privileges. Installer Detection is designed to scan the name and the resources of the EXE to determine whether an application is an installer. For example, an executable would be marked as an installer if the executable name or description contained the strings "install" or "setup". So an application named setup.exe, without an application manifest, would trigger a UAC elevation if launched by a token without administrator privileges.
Another technology under the UAC umbrella designed to help fix file and registry issues at run time is called File and Registry Virtualization. Many applications write their log files or their configuration to the Program Files or Windows directory. These directories do not allow a standard user to write to these locations and hence these writes would immediately fail. In order to fix these issues, File Virtualization takes the files written to certain protected locations in the file system and redirects the writes to the user's profile-it will not perform virtualization if the file being written to is a binary. Registry virtualization does the same thing for the registry by redirecting writes to HKLM\Software to a per-user location in the registry. These technologies have raised the application compatibility for legacy applications immensely.
One important note is that these technologies are disabled if the binary has an application manifest or if the binaries are natively compiled for 64-bit. This is because these technologies are really just crutches for legacy applications that were not designed with the standard user in mind. Unfortunately, these technologies don't solve all of the problems. Next, I'll look at some other common issues.

Problems During Installation
One of the most frequent problems we see is mismarked CustomActions in an MSI file. Many CustomActions do not specify the no impersonate flag msidbCustomActionTypeNoImpersonate, causing the CustomAction to be run as Local System. Because the default token on Windows Vista is now similar to that of a member of the Users group, these MSI files fail because the CustomActions attempt to do something that requires administrator privileges. Usually this can be fixed by merely adding the msidbCustomActionTypeNoImpersonate attribute to the CustomActions.
Another typical problem we've encountered during testing concerns applications that are launched as part of an install. It is very common to start an application at the end of installation. Unfortunately, the application is often started under the wrong user context because the user provided elevated credentials to perform the installation and the application is created with the elevated user token. The recommended guidance for solving this problem is to create a bootstrapper EXE. This bootstrapper may unpack the binaries to a temp location and then start another process that initiates the installation . When the process that started the installation completes, it returns control to the unpacker that starts the application. This way the application is running with the standard user token.
The last installation issue we've often come up against is performing operations that require administrator privileges on the first run of the application. These include copying or updating binaries into Program Files, installing a driver, or any other operation that assumes the user is an administrator. The best way to get a handle on these issues is to test your application when installed and run as a standard user.
Many applications have some mechanism for updating the application binaries. Often these updaters fail because they are running with a lower-privileged token and attempt to overwrite the binaries in the Program Files directory. If your application updates its binaries, move the update functionality to a separate process and mark it as requiring administrator privilege. Another option is to employ MSP files to do the elevation based on the signature of the patch. For this, you would need to populate the MsiPatchCertificate table in your original MSI file.

Typical Runtime Application Issues
One of the most common mistakes that occur at run time is an unnecessary check to see if the user is an administrator. In many games, for example, the first thing checked is whether the user is a member of the administrator group. Often the games will report a failure to the user-but they actually don't ever call any APIs that require administrator privileges! Obviously, this should be avoided. However, for resiliency, if you write an application that requires administrator privileges and is manifested, add this check and exit if the user doesn't have administrator privileges.
Many applications will begin execution and immediately attempt to register a class to HKEY_CLASSES_ROOT (HKCR). Because the applications no longer have administrator privileges, it will be impossible for them to write to this location and the operation will fail. Registering these classes should be done during installation or redirected to the user's classes root: HKCU\Software\Classes.
Another potential issue to look out for is that standard users are prevented from creating objects in the Global namespace. For example, a standard user could not create a Named Pipe or Shared Memory within the namespace. Global is intended only for use by services or applications maintaining cross-session state. The Local namespace should be used instead. This is writable by a standard user.
Using MAX_ALLOWED when opening an object is another issue. In Windows XP, it is generally assumed that the users running the applications were administrators. Therefore, when developers used MAX_ALLOWED when opening files and registry keys, there was never a problem in this environment because they were always opening the files with full administrator rights. In Windows Vista, this has changed. Now, when developers write applications they need to be more careful when specifying the privileges a user will need to open an object.

Conclusion
This article is an overview of the new environment you'll be developing under in Windows Vista. The most fundamental change is that applications now run with a lower-privileged token by default and thus cannot impact the machine or other users. For more information about topics related to UAC in Windows Vista, see the UAC whitepapers mentioned earlier on MSDN and TechNet. And remember that UAC will make the lives of your users and systems administrators a lot easier.

2007-03-25

 

More IT news, prices and information