4

My app needs to impersonate a service account, which I do through a native-call to LogonUser. However, it appears that random components in the .Net library try to access registry keys the account doesn't have access to, causing a SecurityException to be thrown.

Specifically, when I load a LinkLabel, it crashes trying to determine the default hyperlink color in IE:

System.Security.SecurityException: Requested registry access is not allowed.
      at System.ThrowHelper.ThrowSecurityException(ExceptionResource resource)
      at Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean writable)
      at Microsoft.Win32.RegistryKey.OpenSubKey(String name)
      at System.Windows.Forms.LinkUtilities.GetIEColor(String name)
      at System.Windows.Forms.LinkUtilities.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.Control.ControlNativeWindow.OnMessage(Message& m)
      at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
      at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
The Zone of the assembly that failed was:  MyComputer

No, setting the default color does not help.


I found this thread with the exact same problem, but I'm afraid I don't understand the solution:

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.

Does anyone know of any workarounds for this?

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
  • Why do you need to impersonate a service account? It doesn't seem like a good idea. I suggest you find a different way of solving whatever problem prompted you to do that. If the user needs access to some resource, grant access. Or create a COM server service to do the dirty work, and configur it to run as the service account but give your users launch permissions. – Ben Mar 08 '11 at 23:29
  • @Ben: Corporate requirements... – BlueRaja - Danny Pflughoeft Mar 09 '11 at 16:37
  • What is the impersonation actually for? If you tell us what the purpose is we may be able to suggest an alternative way of achieving that purpose. – Ben Mar 09 '11 at 20:50
  • @Ben: Accessing a database that requires a service account to access. No, they won't do user-level permissions, access is restricted at the application level. Yes, I realize how insecure this is. – BlueRaja - Danny Pflughoeft Mar 09 '11 at 21:19
  • OK great! so long as you know ;-) I take it you mean either a custom flat file DB library, or a locked-down SQL server like Sage ACT? My suggestion is a business object/proxy layer running in the service account, accessed via DCOM from the front end app. The business object layer can be launched by DCOM with the service account. Probably too late for this suggestion to help you though! If so I suggest you go with option 1 below, and just impersonate for the duration of the DB access, then revert. /(Also see comment below)/ – Ben Mar 09 '11 at 21:32
  • @Ben, you should make using DCOM an answer (I'll give it vote), it's an easier way of doing my #3 below. – Tony Lee Mar 09 '11 at 23:30
  • @Tony Lee, I did! It got deleted by an admin and is currently on -1 votes. :-) – Ben Mar 10 '11 at 11:20

4 Answers4

3

The problem is you are impersonating too long and your code (indirectly through the .NET framework) is accessing more resources than you intended while impersonating. This exception seems to be caused by the fact your impersonation code is running on a GUI (STA) thread.

You can:

  1. Impersonate for a shorter period of time - only as long as you need to so call impersonate followed by undo as soon as you can. Even if one statement later you have to re-impersonate. This is the typical impersonation pattern.
  2. Move the impersonating code to a worker (MTA) thread and you should avoid this particular symptom. Now you have the problem of how to communicate with the impersonation code but it's not terrible.
  3. If you really want the whole process to run as the system account (as a least privilege sort of thing perhaps), the only supported solution I know of is to have HKEY_CURRENT_USER be something the system account can access. That's done by calling LoadUserProfile and then call CreateProcessAsUser; but that's a whole new architecture for you to spin a new process to handle the impersonation.
Tony Lee
  • 5,622
  • 1
  • 28
  • 45
  • 1 & 2 sound incredibly painful, and 3 is not an option *(the admins would never allow it)*. There is seriously no way to use the impersonated user's HKCU hive? – BlueRaja - Danny Pflughoeft Mar 09 '11 at 17:05
  • 2
    1 is not so hard. You can simplify it by using a class implementing IDispose to do the RevertToSelf.All you ahve to do is something like this: `using(Impersonator i = new Impersonator()){ /* access DB here */ }` – Ben Mar 09 '11 at 21:34
  • Well I ended up doing #1, kind of - I need to impersonate *most of the time*, but not *always,* so I just called `undo()` on the rare occasions I *don't* want to be impersonated, then re-impersonated right away. – BlueRaja - Danny Pflughoeft Jul 14 '11 at 03:57
0

What are you trying to do?

Why do you need to impersonate a service account? It doesn't seem like a good idea. I suggest you find a different way of solving whatever problem prompted you to do that.

If the users need access to some resource, grant access to that group of users.

Or create a COM server service to do the dirty work, and configure it to run as the service account but give your users launch permissions.

Really you mustn't do this. You are blowing a hole as big as a battleship in Windows security.

Ben
  • 34,935
  • 6
  • 74
  • 113
  • Not sure why that was deleted? This is the correct answer guys! Most likely his underlying problem can be solved with judiciously added ACL ACE. – Ben Mar 09 '11 at 21:01
  • +1 for the COM recommendation - this really is a common practice to use a separate service for operations that required increased privileges. An example is the MS installer service. COM makes it easy to launch and leverage. I also agree, this *could* easily create security holes but that's true of any impersonation of a more privileged account. – Tony Lee Mar 10 '11 at 15:04
  • 1
    @Tony @Ben: Since when is COM-anything common practice in .Net? – BlueRaja - Danny Pflughoeft Mar 10 '11 at 17:01
  • It may not be common practice but it is best practice. Did you have any luck with the regedit registry permissions suggestion from yesterday, or are you doing Tony's impersonation fix? – Ben Mar 10 '11 at 17:57
0

It looks to me like the problem is the service account does not have access to HKEY_CURRENT_USER. You may be able to solve the problem by adding an ACE to your HKEY_CURRENT_USER hive granting read access to the service account.

This is also horribly insecure, by the way, if anything even worse. Use Tony Lee's number 1 option if you can get it to work.

Try it in Regedit before you go to the trouble of writing code to do it.

Ben
  • 34,935
  • 6
  • 74
  • 113
0

For this specific problem, I just created a label (instead of a linklabel), made it blue and underlined, and set the Cursor to Cursors.Hand. Then it behaves exactly like a linklabel, except the link color doesn't change by the user's theme (oh well).

I then had other problems with permissions to save files; see my comments to @Tony's answer above for the solution.

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283