My setup is a little complicated so let me go over that first.
We have a WCF web service that gets data from various sources through several different API's and returns that data to the client. The requested security is that it be done over HTTPS(working)
The IIS standard is that the app pool must be set to use the basic IIS network service account and that .net impersonation should be used.
My problem is that the web service should always run under an AD process ID regardless of who calls it, but that it should also check what AD groups the caller is in to determine what functions are accessible. Now I can setup my web.config to use and this kind of works for making it always run as Blah but then I don't know how to also make it impersonate/check the calling user to see what functions they have access too.
**edit: forgot to mention the calling client should be able to pass a UN/PASS instead of just its windows token. The wcf service should verfiy its a valid AD UN and PASS as well as poll what groups it in.
It sounds like you want HostingEnvironment.Impersonate
Example:
using (var imp = HostingEnvironment.Impersonate())
{
// code now executes in the context of the authenticated user,
// rather than the service account
}
That works fantastically, unfortunately the standard here is to not use app pools as password management is easier for them if its on each team to keep them up to date by putting it in the web.config
Well, that seems counter-intuitive, but I've run into worse policies in my day, so I'm hardly one to judge. ;)
As I mentioned in my comment, there are overloads of Impersonate that will allow you to impersonate an arbitrary account. In order to do this, you must obtain the windows identity token for that user, and this is non-trivial, and not, to my knowledge, something that you can do 100% in managed code. You'll have to use unmanaged code, and you'll have to know the username and password of the impersonated account within your application. This is far less secure than simply setting the account as the App Pool ID, if you ever want to argue the point with your network architect, BTW. Just some ammo for ya.
Anyway, here's some example code I adapted from the internets:
#region native imports.
public const int Logon_LogonTypeInteractive = 2;
public const int Logon_ProviderDefault = 0;
public const int Duplicate_ImpersonationLevelImpersonate = 2;
[DllImport("advapi32.dll")]
public static extern bool LogonUser(string lpszUserName, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
#endregion native imports.
#region elsewhere...
public IntPtr GetWindowsTokenForImpersonation(string username, string password, string domain)
{
IntPtr loginToken = IntPtr.Zero;
IntPtr workingToken = IntPtr.Zero;
bool success
if(!RevertToSelf())
{
return IntPtr.Zero;
// failed to eliminate any existing impersonations. This block may not be necessary depending on your code
}
if(!LogonUserA(username, domain, password, Logon_LogonTypeInteractive, Logon_ProviderDefault, ref loginToken))
{
return IntPtr.Zero;
// failed to log in the user
}
if(!DuplicateToken(loginToken, Duplicate_ImpersonationLevelImpersonate, ref workingToken)
{
if(loginToken != IntPtr.Zero)
{
CloseHandle(loginToken);
}
return IntPtr.Zero;
// failed to get a working impersonation token
}
CloseHandle(loginToken);
return workingToken; // NOTE: You must dispose this token manually using CloseHandle after impersonation is complete.
}
#endregion elsewhere
#region where you want to impersonate
var token = GetWindowsTokenForImpersonation(username, password, domain);
if(token != IntPtr.Zero)
{
using(var imp = HostingEnvironment.Impersonate(token))
{
// code here executes under impersonation
}
CloseHandle(token);
}
#endregion