Impersonation in .NET in Windows 7 (Requested registry access is not allowed)
So I am using a user to run the following code that is a member of the "User" group on a Windows 7, x64 machine. I am trying to use impersonation (by logging in as a user that is part of the Administrator group) to allow the current user to read from the registry. For some reason the login happens successfully but even though WindowsIdentity.GetCurrent() is returning the user that is part of the Administrator group I am still getting an error message saying "Requested registry access is not allowed". What am I doing wrong?
This is the main code:
Dim ra As RunAs = Nothing
If UserDomain.Length > 0 AndAlso UserName.Length > 0 AndAlso UserPassword.Length > 0 Then开发者_运维技巧
ra = New RunAs
ra.ImpersonateStart(UserDomain, UserName, UserPassword)
End If
If Not My.Computer.Registry.GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting", "DontShowUI", 0) Is Nothing AndAlso _
My.Computer.Registry.GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting", "DontShowUI", 0) = 0 Then
My.Computer.Registry.SetValue("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting", "DontShowUI", 1)
End If
And suppose my RunAs class is the following:
Public Class RunAs
Public Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Boolean
Public Declare Auto Function DuplicateToken Lib "advapi32.dll" (ByVal ExistingTokenHandle As IntPtr, _
ByVal SECURITY_IMPERSONATION_LEVEL As Integer, _
ByRef DuplicateTokenHandle As IntPtr) As Boolean
' Test harness.
' If you incorporate this code into a DLL, be sure to demand FullTrust.
<PermissionSetAttribute(SecurityAction.Demand, Name:="FullTrust")> _
Public Sub ImpersonateStart(ByVal Domain As String, ByVal userName As String, ByVal Password As String)
tokenHandle = IntPtr.Zero
' Call LogonUser to obtain a handle to an access token.
Dim returnValue As Boolean = LogonUser(userName, Domain, Password, 2, 0, tokenHandle)
'check if logon successful
If returnValue = False Then
Dim ret As Integer = Marshal.GetLastWin32Error()
Console.WriteLine("LogonUser failed with error code : {0}", ret)
Throw New System.ComponentModel.Win32Exception(ret)
Exit Sub
End If
'Logon succeeded
' Use the token handle returned by LogonUser.
Dim newId As New WindowsIdentity(tokenHandle)
impersonatedUser = newId.Impersonate()
End Sub
End Class
I agree with @Hans. With UAC, you need to restart the application with UAC privileges, which will cause the UAC prompt to display. A simple way to accomplish this is as follows:
- In normal path of the application, when admin privileges are needed, restart the application with UAC request and a command-line flag, like
/admin
. - On second run of the application, detect the flag
/admin
, and do the administrative part of the application. - When the second run finishes (#2), if it was successful, then continue application logic from the first pass. If not successful, display error/perform appropriate exception handling logic.
In our application, I have a helper method called RunElevated
that attempts to restart the application with requested administrator privileges, which will cause the UAC prompt to display (I have also included my IsAdmin() helper function):
Private Function RunElevated(commandLine As String, Optional ByVal timeout As Integer = 0) As Boolean
Dim startInfo As New ProcessStartInfo
startInfo.UseShellExecute = True
startInfo.WorkingDirectory = Environment.CurrentDirectory
Dim uri As New Uri(Assembly.GetEntryAssembly.GetName.CodeBase)
startInfo.FileName = uri.LocalPath
startInfo.Verb = "runas"
startInfo.Arguments = commandLine
Dim success As Boolean
Try
Dim p As Process = Process.Start(startInfo)
' wait thirty seconds for completion
If timeout > 0 Then
If Not p.WaitForExit(30000) Then
' did not complete in thirty seconds, so kill
p.Kill()
success = False
Else
success = True
End If
Else
p.WaitForExit()
success = True
End If
Catch ex As Win32Exception
success = False
Catch ex As Exception
MsgBox("Error occurred while trying to start application as administrator: " & ex.Message)
success = False
End Try
Return success
End Function
Public Function IsAdmin() As Boolean
Dim id As WindowsIdentity = WindowsIdentity.GetCurrent
Dim p As New WindowsPrincipal(id)
Return p.IsInRole(WindowsBuiltInRole.Administrator)
End Function
To use, I pass a flag and run elevated. In my case, I have a function that sets registry keys, and uses the flag /setregistry
to indicate that the instance is started for UAC purposes to just set the registry keys. That code looks something like this:
Dim success As Boolean
If Not IsAdmin() Then
' try to create the registry keys as administrator
success = RunElevated("/setregistry", 30000)
success = success And ValidateKeysSet() ' check if it was successful
Return success
End If
' If we are Admin (Not IsAdmin() = False), then go ahead and set the keys here
Then in the startup logic (Form_Load, since this is a forms application), I check if that flag is present:
If Command.ToLower.Contains("/setregistry") Then
' if application instance is for sole purpose of setting registry keys as admin
If IsAdmin() Then
SetRegistryKeys() ' set the keys, since we are admin
Else
MsgBox("ERROR: Application must be run as administrator to set registry keys.")
End If
Else
' Perform normal startup process
End If
精彩评论