Secure windows Impersonation?
Asked Answered
H

3

13

In my WPF application I want to allow administrators to test a database connection using integrated security for various other users. So I have a form that allows the admin to enter the domain, username and password and then test it. I am able to securely handle the password right up until I call LogonUser in the advapi32.dll which takes a string password

LogonUser(UserName, Domain, Password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref UserHandle)

I have written a utility function which converts the SecureString to a string as safe as possible, and then im calling it on the password in the LogonUser call:

LogonUser(UserName, Domain, Helper.ConvertSafely(Password), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref UserHandle)

Since the signature for LogonUser takes a string, unless LogonUser is taking proper care of the password in its execution, it could still be on my call stack in plain text after the call returns. Is there a more secure way to impersonate a user in which i can be confidant the PW is secure the whole time?

Basically all I need is a WindowsImpersonationContext but i would like to aquire it without the password ever being in plain text.

Hardi answered 3/9, 2014 at 13:55 Comment(8)
I have edited your title. Please see, "Should questions include “tags” in their titles?", where the consensus is "no, they should not".Terr
Good question, actually. But if you're truly concerned about security, then I suggest that you also consider non-technical security leaks: Your administrators must not have, nor require, any knowledge about your users' passwords. The fact that you're creating a username/password screen suggests that the administratos will enter user credentials. This seems very wrong to me. (And if it's not admins, but the users themselves, who will fill in their credentials, then just RUNAS the application under their own login in the first place, and you won't need to do impersonation.)Dockery
Won't the original form input field be using a regular string anyway? Also, you may be interested in simplifying your LogonUser code with my SimpleImpersonation library, as described here. Though I don't use SecureString there either.Domicile
@stakx the connection will be used by a service to connect, so the admin should have the credentials for the user that the service will be running under. However the admin will be running under his own usercontext while he is configuring the connection.Hardi
If the user you want to impersonate is not logged on the machine (for example in another session), you'll have to provide a password to LogonUser (in the stack, in memory) for standard auth packages. So if you really want impersonation, somewhere you'll have to pass a password around. (if he's logged here is an answer: #8304898). Otherwise makr sure you use postlagerkarte's sample link code for this. You can't really do much more.Chairman
Consider if not dealing with passwords is an option - if you get your program to work when started with "run as" you may just rely on Windows itself to deal with logging in...Gamecock
@SimonMourier Thanks Simon for the answer, if you would like to post it as an answer i will gladly accept it since it did answer the question that was asked.Hardi
how do you know the way logonuser sends the password to the activedirectory? If it's clear text or tls? How can you encrypt the communication over the network when using native functions and choose the authentication type?Topsail
C
4

If the user you want to impersonate is already logged on the machine (for example in another session), here is an answer: Is it possible for a Windows service impersonate a user without a password?

If he's not, you'll have to provide a password to LogonUser. And this password will have to reside at least a small amount of time, as is, in memory because that's the way LogonUser is defined. Note not all auth packages require passwords (like biometrics or smart card: Windows Authentication Overview).

So if you really want impersonation, somewhere you'll have to pass a password around. In this case make sure you use postlagerkarte's code from Microsoft for this: Marshal.SecureStringToGlobalAllocUnicode Method sample code

You can't really do much more.

Chairman answered 11/9, 2014 at 5:49 Comment(0)
O
6

Inside your managed application you can make use of the SecureString class to handle sensitive information. There is usually no need to roll your own secure string mechanism.

Just before you pinvoke into LogonUser you are then passing the unmanaged copy of the SecureString password. You use the Marshal.SecureStringToGlobalAllocUnicode Method for this. Thus there is never a managed object in your app domain representing the password.

You can find example code in this article.

If you require even more security I would suggest to disallow direct database connection from you wpf clients. You can introduce a middle tier. Security is thus shifted away from the clients to this one server. Communication between Client and App-Server is encrypted and only the app server talks to the database.

Overshoe answered 3/9, 2014 at 14:8 Comment(3)
this is exactly what I am doing, im just concerned that the LogonUser call will leave the unsecured password on the stack which is out of my hands.Hardi
yes you are right. you might want to make it more clear in your question. It is always possible to set a breakpoint on the API LogonUser entry and dump the Call Stack. So the question is more around the native LogonUser function.Overshoe
Im not sure i can make the question much more clear, I tried. But yes the concern is with the unmanaged code LogonUser, and if there is a more secure method to acquire a WindowsImpersonationContext without having to worry about how the unmanaged code is handling the password, which it appears to be using it in plain text.Hardi
C
4

If the user you want to impersonate is already logged on the machine (for example in another session), here is an answer: Is it possible for a Windows service impersonate a user without a password?

If he's not, you'll have to provide a password to LogonUser. And this password will have to reside at least a small amount of time, as is, in memory because that's the way LogonUser is defined. Note not all auth packages require passwords (like biometrics or smart card: Windows Authentication Overview).

So if you really want impersonation, somewhere you'll have to pass a password around. In this case make sure you use postlagerkarte's code from Microsoft for this: Marshal.SecureStringToGlobalAllocUnicode Method sample code

You can't really do much more.

Chairman answered 11/9, 2014 at 5:49 Comment(0)
V
1

Update:

You could implement an encrypted app.config. You can do this through several different approaches:

So how this would work, is when an Administrator inserts the credentials you would decrypt the app.config modify the key/value with the new credentials. Then once it is inserted it encrypts the config file again.

So it is exposed minimally, but also the data is inserted through encapsulation which helps hide it as well.

This is one approach, though a SecureString will work it expects a char[]. Which in some instances may not be ideal when interfacing through the advapi32.dll.

Valladares answered 3/9, 2014 at 14:12 Comment(4)
I'm confused. How do the encrypted credentials get passed to LogonUser from the config?Hoedown
the security in my code is not an issue, the password is stored in a SecureString, it is when I call the unmanaged code LogonUser it is using the password in plain text.Hardi
@Hardi So the unmanaged code is actually calling it in Plain Text?Valladares
the signature of LogonUser takes a string: public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); I am converting the secureString using the method Postlagerkarte suggested within the function callHardi

© 2022 - 2024 — McMap. All rights reserved.