Reputation: 65
I have a windows service that grabs a data set via SQL connection string from a different server. I need to impersonate a specific user account that has access to this SQL Server database. The code works find if I build it as a console application, but a windows service messes things up.
When building as a windows service, no matter the impersonation, the service tries to connect and authenticate using the machine account (which is wrong) as designated by the error:
Cannot open database \"DATABASE\" requested by the login. The login failed. Login failed for user 'DOMAIN\MACHINENAME'.
With this snippet of code I use the impersonator class:
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public DataSet GetDataSetSQL(string pSQL)
{
DataSet ds = null;
DbDataAdapter adapter = null;
try
{
using (impers = new Impersonator(impers_uname, impers_domain, impers_password))
{
/// Create connection
using (DbConnection conn = this.factory.CreateConnection())
{
conn.ConnectionString = this.connectionString;
/// Create Command
using (DbCommand cmd = conn.CreateCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = pSQL;
adapter = this.factory.CreateDataAdapter();
adapter.SelectCommand = cmd;
ds = new DataSet();
adapter.Fill(ds);
}
}
}
}
finally
{
if (adapter != null) adapter = null;
}
return ds;
}
Here is where I get the token:
if (LogonUser(
userName,
domain,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
I could just run the windows service under the user account, but I want to have that extra level of granularity and also portability.
Anyone have any ideas how impersonation is different between applications and windows services?
Edit:
New enums and logonuser function call
public enum LogonType
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK = 3,
LOGON32_LOGON_BATCH = 4,
LOGON32_LOGON_SERVICE = 5,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT = 8, // Win2K or higher
LOGON32_LOGON_NEW_CREDENTIALS = 9 // Win2K or higher
};
public enum LogonProvider
{
LOGON32_PROVIDER_DEFAULT = 0,
LOGON32_PROVIDER_WINNT35 = 1,
LOGON32_PROVIDER_WINNT40 = 2,
LOGON32_PROVIDER_WINNT50 = 3
};
if (LogonUser(
userName,
domain,
password,
(int)LogonType.LOGON32_LOGON_NEW_CREDENTIALS,
(int)LogonProvider.LOGON32_PROVIDER_WINNT50,
ref token) != 0)
Upvotes: 3
Views: 2374
Reputation: 6050
Regards to the 4th parameter the function LogonUser, you're using LOGON32_LOGON_INTERACTIVE.
This logon type is intended for users who will be interactively using the computer, such as a user being logged on by a terminal server, remote shell, or similar process. but Windows service is not in this category, so I thing you should use LOGON32_LOGON_SERVICE instead.
Upvotes: 1