3

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)
gofr1
  • 15,741
  • 11
  • 42
  • 52
Abyssul
  • 65
  • 1
  • 6
  • See this [other SO q&a](http://stackoverflow.com/questions/559719/windows-impersonation-from-c-sharp). – Marcel N. Aug 04 '14 at 19:16
  • What specifically from this link? This was the first link I ran across when searching, but it didn't seem helpful. – Abyssul Aug 04 '14 at 19:59
  • The CodeProject links in [this answer](http://stackoverflow.com/a/559740/1119545) (maybe the MSDN link as well). The first comment to that is that it works. – Marcel N. Aug 04 '14 at 20:01
  • Why not give the service account a login to the server, this is typical setup. – Hogan Aug 04 '14 at 20:23
  • Like I mentioned in my original post, this works when it is a console application, but not when its compiled into a windows service. My service followed that link to the dot. The person who said that it works was not the OP so who knows what scenario he/she used it for. – Abyssul Aug 04 '14 at 20:25
  • oh... you have to run the service as a specific user https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/sys_srv_logon_user.mspx?mfr=true – Hogan Aug 04 '14 at 20:27
  • Hogan, I know about that feature. Using impersonation allows more granularity for me in my code and also allows the service to be installed on servers without giving out the password. – Abyssul Aug 04 '14 at 20:31

1 Answers1

1

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.

Matt
  • 6,010
  • 25
  • 36
  • I've tried using that logon type and got "Logon failure: the user has not been granted the requested logon type at this computer." – Abyssul Aug 04 '14 at 19:29
  • try change it to LOGON32_LOGON_NEW_CREDENTIALS, while you also need to change the 5th parameter to LOGON32_PROVIDER_WINNT50 – Matt Aug 04 '14 at 19:34
  • Set to `private const int LOGON32_LOGON_INTERACTIVE = 9; private const int LOGON32_PROVIDER_DEFAULT = 3;` Same error. – Abyssul Aug 04 '14 at 20:03
  • I did. LOGON32_LOGON_NEW_CREDENTIALS translates to 9, and LOGON32_PROVIDER_WINNT50 translates to 3. Am I missing something? Doesn't matter what I call the variables. – Abyssul Aug 04 '14 at 20:08
  • LOGON32_LOGON_INTERACTIVE and LOGON32_PROVIDER_DEFAULT are defined by Windows header file, you should not use these as variable names. – Matt Aug 04 '14 at 20:11