Impersonation and LinkLabels result in SecurityException

J

JP

Hello,

we have an app developed with VS.NET and C#. The user interface has a
login-screen, which performs login and impersonation of given
Windows-user. The impersonation token is created with a P/Invoke on
::LogonUser() and the actual impersonation is accomplished by .NET
framework's WindowsIdentity.Impersonate(). This part works OK.

However, when impersonation is active, and a link label control is
drawn onscreen, a SecurityException is thrown stating: "Requested
registry access is not allowed". From the call stack (shown below) we
can see, that the control attempts to read some values from the
computer's registry -- and fails.

The impersonated user belongs in Users-group, plus a few custom groups
we have created for the application. If the same user logs into
Windows and starts the app without impersonation, everything works OK,
no exceptions are thrown.

If the user is added to Administrators-group, the impersonation works.
However, this is no solution.

I have read Q820637 from the MS Knowledge Base, but it did not solve
the problem.

Does anybody else have similar experiences? Any thoughts on why the
registry access fails when impersonating, but works when logged in?
Could it be that the original user context has somehow locked the rows
in the registry, so that when the impersonated user tries to access
them, a sharing violation occurs.

Thx,

-JP

---clip---

System.Security.SecurityException: Requested registry access is not
allowed.
at Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean
writable)
at Microsoft.Win32.RegistryKey.OpenSubKey(String name)
at System.Windows.Forms.LinkLabel.GetIEColor(String name)
at System.Windows.Forms.LinkLabel.get_IELinkColor()
at System.Windows.Forms.LinkLabel.get_LinkColor()
at System.Windows.Forms.LinkLabel.OnPaint(PaintEventArgs e)
at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs
e, Int16 layer, Boolean disposeEventArgs)
at System.Windows.Forms.Control.WmPaint(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.Label.WndProc(Message& m)
at System.Windows.Forms.LinkLabel.WndProc(Message& msg)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr
hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG&
msg)
at System.Windows.Forms.ComponentManager.System.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager.FPushMessageLoop(Int32
dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at System.Windows.Forms.ThreadContext.RunMessageLoop(Int32 reason,
ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
 
P

Philip Rieck

Trying to repro this, and have a few questions:

1) what are you passing to logonUser's LogonType parameter? You should be
requesting an interactive (primary)token. (2)
2) are you duplicating the security token using DuplicateToken() (in
advapi.dll), and passing the duplicate token to Impersonate? If so, what
are you passing to DuplicateToken's SecurityImpersonationLevel param? You
should be duplicating and requesting level 2 (primary token)
3) are you closing the original (non-duplicated) token using closehandle?
(I've always closed it before calling impersonate )

just for repro information:
4) Is the account the thread is originally running under (before
impersonation) an administrator?
5) Can you run sysinternal's "regmon" and see what it's trying to do?
http://www.sysinternals.com/ntw2k/source/regmon.shtml
6) Is the linklabel created (even if not shown) before the call to
impersonate?


My guess (if 1-3 above are OK), is that the link label is trying to open the
wrong registry key, or is initializing its state and assuming a specific
user or privledge based on the original thread's credentials, but I'm not
sure yet.


-Philip
 
W

Willy Denoyette [MVP]

Which hive or key are you reading from?

Willy.


JP said:
Hello,

we have an app developed with VS.NET and C#. The user interface has a
login-screen, which performs login and impersonation of given
Windows-user. The impersonation token is created with a P/Invoke on
::LogonUser() and the actual impersonation is accomplished by .NET
framework's WindowsIdentity.Impersonate(). This part works OK.

However, when impersonation is active, and a link label control is
drawn onscreen, a SecurityException is thrown stating: "Requested
registry access is not allowed". From the call stack (shown below) we
can see, that the control attempts to read some values from the
computer's registry -- and fails.

The impersonated user belongs in Users-group, plus a few custom groups
we have created for the application. If the same user logs into
Windows and starts the app without impersonation, everything works OK,
no exceptions are thrown.

If the user is added to Administrators-group, the impersonation works.
However, this is no solution.

I have read Q820637 from the MS Knowledge Base, but it did not solve
the problem.

Does anybody else have similar experiences? Any thoughts on why the
registry access fails when impersonating, but works when logged in?
Could it be that the original user context has somehow locked the rows
in the registry, so that when the impersonated user tries to access
them, a sharing violation occurs.

Thx,

-JP

---clip---

System.Security.SecurityException: Requested registry access is not
allowed.
at Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean
writable)
at Microsoft.Win32.RegistryKey.OpenSubKey(String name)
at System.Windows.Forms.LinkLabel.GetIEColor(String name)
at System.Windows.Forms.LinkLabel.get_IELinkColor()
at System.Windows.Forms.LinkLabel.get_LinkColor()
at System.Windows.Forms.LinkLabel.OnPaint(PaintEventArgs e)
at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs
e, Int16 layer, Boolean disposeEventArgs)
at System.Windows.Forms.Control.WmPaint(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.Label.WndProc(Message& m)
at System.Windows.Forms.LinkLabel.WndProc(Message& msg)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr
hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG&
msg)
at
System.Windows.Forms.ComponentManager.System.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager.FPushMessageLoop(Int32
dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at System.Windows.Forms.ThreadContext.RunMessageLoop(Int32 reason,
ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
 
J

JP

Hello,

1)-3) LogonType is Interactive, and yes, I am duplicating the token
with the SecurityImpersonationLevel param. I was missing a call to
CloseHandle(), but adding it didn't have any effect.

4) Yes, the original account has Administrator privileges.

5) RegMon revealed that the error arises from referring to the
HKEY_CURRENT_USER
root key. In both cases (impersonating or not) this maps to the same
physical key.

When impersonating, the RegMon output is as follows:

Request Path Result Other
-----------------------------------
QueryKey HKCU SUCCESS Name:
\REGISTRY\User\S-1-5-21-68842957-1183140953-226366656-1437_Classes
OpenKey HKCU ACCDENIED Access: 0x20019 L0319OUS\ImpersonatingUser
OpenKey HKCU ACCDENIED Access: 0x2000000 L0319OUS\ImpersonatingUser

And in the case of not impersonating:

Request Path Result Other
-----------------------------------
QueryKey HKCU SUCCESS Name:
\REGISTRY\User\S-1-5-21-68842957-1183140953-226366656-1437_Classes

The key at issue has Read-permissions for the original user,
Administrators-group and users SYSTEM and RESTRICTED.

6) The controls are created beforehand, but shown afterwards. Moving
the creation of the controls after the impersonation doesn't have
effect.


So, it doesn't seem to be a problem with the link label, but rather a
problem with the .NET registry access functions. The same exception is
raised by doing something like

RegistryKey key = Registry.CurrentUser.OpenSubKey("Console");

after impersonation.

I created a similar case with C++, impersonating the user and trying
to open the same key, HKCU/Console. With native Win32 registry calls
(RegOpenKey, RegQueryValueEx), the HKCU root key points to the correct
place, i.e. the user being impersonated.

Substituting the actual impersonation call from
WindowsIdentity.Impersonate() to a P/Invoke on
ImpersonateLoggedOnUser() has no effect.

I'll look into it some more, but as of now it would seem that a bug or
something similar in the .NET Microsoft.Win32.Registry class (or a
related class) is causing the exception.

Anyway, thanks for your help!

-JP
 
J

JP

Hi,

see my recent post.

The problem seems to be arising from the .NET frameworks
Microsoft.Win32.Registry class or a related class. Even after
impersonation, the Microsoft.Win32.Registry.CurrentUser property still
points to the original user's registry hive. This causes the security
exception -- the LinkLabel control is reading something from
HKEY_CURRENT_USER.

A similar construct on C++ and Win32 API works, so the I believe the
problem lies definitely in the .NET framework.

-JP
 
P

Philip Rieck

See below...
5) RegMon revealed that the error arises from referring to the
HKEY_CURRENT_USER
root key. In both cases (impersonating or not) this maps to the same
physical key.

So, it doesn't seem to be a problem with the link label, but rather a
problem with the .NET registry access functions. The same exception is
raised by doing something like

RegistryKey key = Registry.CurrentUser.OpenSubKey("Console");

after impersonation.


I'll look into it some more, but as of now it would seem that a bug or
something similar in the .NET Microsoft.Win32.Registry class (or a
related class) is causing the exception.


That's your problem -- the HKCU hive loaded is for the original user
(Administrator), not for the impersonated user -- when impersonating, the CU
hive (and other profile items that are not persistently available) is not
loaded. Try calling LoadUserProfile (and UnloadUserProfile) to get the HKCU
for that user loaded.

This is why if the user is an admin it works -- they are actually accessing
the HKCU for the original user, which only an admin would have rights to do.

I'd call this more of an "almost-bug" in the LinkLabel -- most likely, it is
trying to figure out the user's default browser choice. I would have
expected it to fail gracefully in this kind of situation, but I guess not.
You may want to try creating your own LinkLabel that uses the shell to
execute the URL.
 
W

Willy Denoyette [MVP]

The way you impersonate doesn't load the registry hive of the impersonating
user, so HKCU is still the interactive logon user's hive, this is something
you need to do explicitly by calling LoadUserProfile before impersonating.
When done you need to call UnloadUserProfile.
Why it works in C++ is kind of a mystery, but I guess one way or another you
did load the hive.

Willy.
 
J

JP

Hello,

thanks for your reply.

I added a call to LoadUserProfile(), but it did not solve the problem.
Microsoft.Win32.Registry.CurrentUser still seems to be pointing to the
original user's hive.

Does the LoadUserProfile() method really do anything else than return
the registry key pointing to the specified user's registry hive?

Thx,

-JP
 
J

JP

Hello Philip,

I added the call to LoadUserProfile(), but it did not solve the
problem. If I understand this correctly, it returns a registry key
pointing to the impersonated user's hive. MSDN states that "If a
service that is impersonating a user needs to read or write to the
user's registry file, use this handle instead of HKEY_CURRENT_USER."

Now, in C++, this would be a piece of cake.

But using .NET, the property Microsoft.Win32.Registry.CurrentUser
still points to the original user's hive. Any registry operation into
the aforementioned hive after impersonation results in an exception.
And there seems to be no way to update the
Microsoft.Win32.Registry.CurrentUser-property; it is read-only and
there are no methods to reinitialize it (that I could find, that is).

Furthermore, the value returned by the LoadUserProfile() function is a
handle (or IntPtr in C#). This really doesn't help at all with regards
to the .NET framework RegistryKey-class, as there doesn't seem to a
way of constructing a RegistryKey-object from handle. This issue could
be easily solved by a custom RegistryKey class and P/Invokes, but it
wouldn't help in the case with the LinkLabel.

As I mentioned earlier (or did I, not 100% certain :)) the original
problem doesn't lie with the LinkLabel control, but rather the
registry classes in .NET. So developing a custom LinkLabel control
would solve this particular issue, but still, any framework or 3rd
party components referring to the HKCU hive would function erroneously
and require similar treatment.

Thx,

-JP
 
W

Willy Denoyette [MVP]

Registry hives loaded with LoadUserProfile are stored under HKU, HKCU
remains the interactive logon user's hive (loaded by winlogon.exe).
So if you need to get at the newly loaded hive you need to:
- set Regkey to Registry.Users
- Open the subkey using the string SID of the user account you are
impersonating.

Willy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top