Quantcast
Channel: Fredrik Haglund's blog » UAC
Viewing all articles
Browse latest Browse all 7

Vista User Account Control and Command Line Applications

$
0
0

Prauge Delphi DayThe last weeks have been quite hectic, I have been face-to-face with over a thousand Delphi developers talking about Delphi 2007 for Win32 in Stockholm, Gothenburg (thanks Karl-Otto for the CodeGeare cap), Budapest, Prauge (see picture), Helsinki, Copenhagen and Oslo.

Jason Vokes tagged me and challenged me to write about five things I like with the new Delphi release:

  1. Like many other already have said, Delphi 2007 launches fast and has a quick and responsive IDE.
  2. Second out are some small debugger features that are really usefull – You can set breakpoints in the call stack and stop execution when it returns to a specific stack frame and you can also select some code and drag and drop it to the watches windows for evaluation.
  3. All RTL and VCL improvements like the MainFormOnTaskbar property on TApplication that solves the long existing problem with the application context menu, Flip3D and Thumbnail support. Improvement of DoubleBuffered rendering. Usage of themed components in IDE. Updated memory manager, etc.
  4. The much improved help system.
  5. And finally another small productivity feature – you can save several diffrent configurations of compiler and linker settings and easily switch between them with the Configuration Manager.

I talked about User Account Control (UAC) during my CodeRage-session last week. I think UAC is one of the most important features of Windows Vista and if you are not in total control of your users’ environment, sooner or later you will run into questions and support issus related to UAC.

UAC Horror Story
I heard a real horror story related to UAC from a developer in Sweden last week during the Delphi User Group meeting in Stockholm. Their application has a local database and the database engine is launched by the application. Since UAC made the application start as Standard User the database enginge also started as Standard User. But a Standard User did not have the rights needed to modify the database files where they got installed. You would assume that this would generate an Access Denied error or some other error message? But no, and now the horror begins…

Since their application did not have required execution level information in the manifest Windows Vista runs the process in compatibility mode. Instead of failing to open the database which the process has no right to modify Windows Vista silently makes a copy of the whole database (to the personal VirtualStore for each user) and saves the changes there instead. The users did not notice anything initally because they work on diffrent projects but after a while they started to wonder why they could not see any changes their colleagues have done.

When they found out what was wrong they had to do a lot of manual work to merge each users’ copy of the database back into one database again…

Conclusion: To get rid of the dangerous compatibility feature called Virtualization or Redirection by adding a manifest with required execution level to all you executables immediately! This can be done easily by just recompiling your application in Delphi 2007 for Win32 and checking use themes in project options or use the xpman unit in D2007. With an earlier Delphi release you can manually create and include the manifest.

Command Line Applications
I did not talk so much about command line applications and UAC during my Code-Rage session so I would like to write a little about that.

First, like with all applications targeting Windows Vista, you should have a manifest with required execution level to turn off compatibility mode. But even if you command line tool requires admin privileges you should never set level = “requireAdministrator”. The reason for this is that you do not want a batch script to be interrupted with a modal consent dialog, instead Microsoft recommends you to halt execution with an error message and set the exit code to ERROR_ELEVATION_REQUIRED.

program VistaConsoleApplication;

{$APPTYPE CONSOLE}

//Include Manifest with trustinfo to make executable Vista Logo Compliant and not run in compatibility mode.
//Console Applications should always run as invoker even if they require admin privileges.
{$R ‘ExecutionLevelAsInvokerManifest.res’ ‘ExecutionLevelAsInvokerManifest.rc’}

uses
SysUtils,
UserAccessControlUtils in ‘UserAccessControlUtils.pas';

begin
//Call function to test we have admin privelieges
RequireAdminConsole;

//TODO: Your admin tool here…
end.

The RequireAdminConsole function is really simple and it uses a function IsUserAnAdmin in the WinAPI to test. I have written the code so it binds dynamically. The reason is to make it work on operation systems before Windows 2000. You could also use a NT4 compatible apporoach and check if token has SID for admin group but that would be much more code…

const
ModName = ‘shell32.dll’;
var
hShell32: HMODULE;
_IsUserAnAdmin: function(): BOOL; stdcall;

function IsUserAnAdmin: Boolean;
begin
if Assigned(_IsUserAnAdmin) then
Result := _IsUserAnAdmin()
else
begin
Result := True;
if hShell32 = 0 then
hShell32 := LoadLibrary(ModName);
if hShell32 <> 0 then
begin
_IsUserAnAdmin := GetProcAddress(hShell32, ‘IsUserAnAdmin’); // Do not localize
if Assigned(_IsUserAnAdmin) then
Result := _IsUserAnAdmin();
end;
end;
end;

resourcestring
SErrorElevationRequired = ‘Access Denied. Administrator permissions are needed to use the selected options. Use an administrator command prompt to complete these tasks.’;
const
ERROR_ELEVATION_REQUIRED = 740;

procedure RequireAdminConsole;
begin
if not IsUserAnAdmin then
begin
WriteLn(SErrorElevationRequired);
Halt(ERROR_ELEVATION_REQUIRED);
end;
end;

A new question
The code above just checks if your process is currently running with admin privileges. But with UAC there is also another question – can the user become an administrator?

Since UAC splits the login token into a user token and a full token during login (if you have admin priveliges) you need to use new WinAPI features to find the answer to this question.

type
//Enumeration is mirroring TOKEN_ELEVATION_TYPE in Windows Vista SDK (except first value).
TTokenElevationType = (TokenElevationNotAvailable, TokenElevationTypeDefault, TokenElevationTypeFull, TokenElevationTypeLimited);

//Extend existing enumeration in Windows.pas with new Vista constants
TTokenInformationClass = (TokenUser = 1, TokenGroups, TokenPrivileges,
TokenOwner, TokenPrimaryGroup, TokenDefaultDacl, TokenSource, TokenType,
TokenImpersonationLevel, TokenStatistics, TokenRestrictedSids, TokenSessionId,
TokenGroupsAndPrivileges, TokenSessionReference, TokenSandBoxInert, TokenAuditPolicy,
TokenOrigin, TokenElevationType, TokenLinkedToken, TokenElevation, TokenHasRestrictions,
TokenAccessInformation, TokenVirtualizationAllowed, TokenVirtualizationEnabled,
TokenIntegrityLevel, TokenUIAccess, TokenMandatoryPolicy, TokenLogonSid);

function GetTokenElevationType: TTokenElevationType;
var
hToken: THandle;
elevationType: Integer;
dwSize: DWORD;
begin
Result := TokenElevationNotAvailable;
hToken := 0;
Win32Check(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, hToken));
if GetTokenInformation(hToken, Windows.TTokenInformationClass(TokenElevationType), @elevationType, sizeof(elevationType), dwSize) then
Result := TTokenElevationType(elevationType);
if hToken<>0 then
CloseHandle(hToken);
end;

Call the function GetTokenElevationType above to test the current state of elevation.

case GetTokenElevationType of
TokenElevationNotAvailable:
WriteLn(‘No information about elevation is available. (This is a non-vista machine)’);
TokenElevationTypeDefault:
WriteLn(‘TokenElevationTypeDefault – User is not using a split token.’);
TokenElevationTypeFull:
WriteLn(‘TokenElevationTypeFull – User has a split token, and the process is running elevated.’);
TokenElevationTypeLimited:
WriteLn(‘TokenElevationTypeLimited – User has a split token, but the process is not running elevated.’);
end;

You can download the example application here. Please, share if you find any problems with the code above or if you have your UAC horror stories of you own!


Viewing all articles
Browse latest Browse all 7

Trending Articles