Home All Groups Group Topic Archive Search About

Mulples threads and impersonation

Author
15 Jun 2006 12:46 AM
David Cablalero
I have a windows service which every night checks a SQL Server database for
some data and business rules. The application can access different DBs with
the same structure, to tell the service which database to check I created
local users and assigned each of them a different default DB in SQL Server,
then, in the windows service I impersonate each user and then access the DB,
when the connection to the DB is made, the default DB for the impersonated
user is automatically selected, so far so good.

Now, after that, I realized that each time I connect to the DB to do the
checking, the code can take a long time to finish and impersonate the next
user, so I decided to put the code that does the impersonating and the
checking in a thread, so the service now creates a different thread for each
user it has to impersonate and then the thread impersonates its assigned
user and runs the checking process, my idea with this was that every DB
could be checked simultaneously, instead of sequentially.

Problem is, its not working!!! It seems every time one of the threads
impersonates a user, it "overwrites" the last impersonation.

Is there no way to have each thread impersonate a diferent user at the same
time??

I'm using Windows Server 2003, SQL Server 2005, Visual Studio.NET 2003 (if
necessary to upgrade to 2005, no problem!), but code should run on Windows
2000 server and SQL Server 2000 also.

Please help!!!

Thanks!
David C.

Author
15 Jun 2006 1:24 AM
Carl Daniel [VC++ MVP]
Show quote
"David Cablalero" <dcaball***@educate-global.com> wrote in message
news:%23XfhiVBkGHA.4304@TK2MSFTNGP03.phx.gbl...
>I have a windows service which every night checks a SQL Server database for
>some data and business rules. The application can access different DBs with
>the same structure, to tell the service which database to check I created
>local users and assigned each of them a different default DB in SQL Server,
>then, in the windows service I impersonate each user and then access the
>DB, when the connection to the DB is made, the default DB for the
>impersonated user is automatically selected, so far so good.
>
> Now, after that, I realized that each time I connect to the DB to do the
> checking, the code can take a long time to finish and impersonate the next
> user, so I decided to put the code that does the impersonating and the
> checking in a thread, so the service now creates a different thread for
> each user it has to impersonate and then the thread impersonates its
> assigned user and runs the checking process, my idea with this was that
> every DB could be checked simultaneously, instead of sequentially.
>
> Problem is, its not working!!! It seems every time one of the threads
> impersonates a user, it "overwrites" the last impersonation.
>
> Is there no way to have each thread impersonate a diferent user at the
> same time??
>
> I'm using Windows Server 2003, SQL Server 2005, Visual Studio.NET 2003 (if
> necessary to upgrade to 2005, no problem!), but code should run on Windows
> 2000 server and SQL Server 2000 also.

Impersonation is strictly a per-thread operation - there's no way to change
the identity of every thread in a process en-masse.

How are you doing the impersonation?  How are you assessing that it's "not
working"?  Have you checked in SQL Server (e.g. via SQL Profiler) to see if
the requests are being made by the impersonated users?

-cd
Author
15 Jun 2006 3:06 PM
DCaballero
Hi Carl, thanks for replying!
Here are some code extracts:

Public Class LMSService
    Inherits ServiceBase

    Private Class DoTaskClass
        Private sUser As String, sDomain As String, sPwd As String

        Public Sub New(ByVal sD As String, ByVal sU As String, ByVal sP
As String)
            sDomain = sD
            sUser = sU
            sPwd = sP
            TaskThread = New Threading.Thread(AddressOf DoTask)
        End Sub

        Private Sub DoTask()
            try
                tokenHandle = IntPtr.Zero
                Dim returnValue As Boolean = LogonUser(sUser, sDomain,
sPwd, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, tokenHandle)
                Dim newId As New WindowsIdentity(tokenHandle)
                impersonatedUser = newId.Impersonate()

                'The checking code

            Catch ex As Exception
                WriteLog("'" + sDomain + "\" + sUser + "':" +
ex.Message + vbCrLf + vbCrLf + ex.StackTrace)
            Finally
                If Not IsNothing(impersonatedUser) Then
                    impersonatedUser.Undo()
                    impersonatedUser = Nothing
                End If
                If Not System.IntPtr.op_Equality(tokenHandle,
IntPtr.Zero) Then
                    CloseHandle(tokenHandle)
                End If
            End Try
        End Sub

        Public Sub StartTask()
            TaskThread.Start()
        End Sub
    End Class


    Private WithEvents LMSServiceTimer As Timer

    Private Sub LMSTimer(ByVal sender As Object, ByVal e As
ElapsedEventArgs) Handles LMSServiceTimer.Elapsed
        Dim LMSTask As DoTaskClass

        if (DateLastCheck<DateToday) then
            for each user to impersonate
                LMSTask = New DoTaskClass(sDomain,sUser,sPwd)
                LMSTask.StartTask
            next
        then
    End Sub

    Protected Overrides Sub OnStart(ByVal args() As String)
        LMSServiceTimer.Enabled = True
    End Sub

    <MTAThread()> _
    Public Shared Sub Main()
        Dim ServicesToRun() As System.ServiceProcess.ServiceBase

        ServicesToRun = New System.ServiceProcess.ServiceBase() {New
LMSService}
        System.ServiceProcess.ServiceBase.Run(ServicesToRun)
    End Sub

End Class

Of course I skipped a lot of things here, like the imports of the
impersonating functions, not creating new threads for the same users,
checking if the threads have finished, etc. I only put the code of the
problem I'm talking about.

I've been testing with only 2 users/databases, after the run I check
the databases and only 1 of them was affected, which one was affected
varies, I'm guessing depending on the order in which the threads did
the impersonation.

Carl Daniel [VC++ MVP] wrote:
Show quote
> "David Cablalero" <dcaball***@educate-global.com> wrote in message
> news:%23XfhiVBkGHA.4304@TK2MSFTNGP03.phx.gbl...
> >I have a windows service which every night checks a SQL Server database for
> >some data and business rules. The application can access different DBs with
> >the same structure, to tell the service which database to check I created
> >local users and assigned each of them a different default DB in SQL Server,
> >then, in the windows service I impersonate each user and then access the
> >DB, when the connection to the DB is made, the default DB for the
> >impersonated user is automatically selected, so far so good.
> >
> > Now, after that, I realized that each time I connect to the DB to do the
> > checking, the code can take a long time to finish and impersonate the next
> > user, so I decided to put the code that does the impersonating and the
> > checking in a thread, so the service now creates a different thread for
> > each user it has to impersonate and then the thread impersonates its
> > assigned user and runs the checking process, my idea with this was that
> > every DB could be checked simultaneously, instead of sequentially.
> >
> > Problem is, its not working!!! It seems every time one of the threads
> > impersonates a user, it "overwrites" the last impersonation.
> >
> > Is there no way to have each thread impersonate a diferent user at the
> > same time??
> >
> > I'm using Windows Server 2003, SQL Server 2005, Visual Studio.NET 2003 (if
> > necessary to upgrade to 2005, no problem!), but code should run on Windows
> > 2000 server and SQL Server 2000 also.
>
> Impersonation is strictly a per-thread operation - there's no way to change
> the identity of every thread in a process en-masse.
>
> How are you doing the impersonation?  How are you assessing that it's "not
> working"?  Have you checked in SQL Server (e.g. via SQL Profiler) to see if
> the requests are being made by the impersonated users?
>
> -cd
Author
16 Jun 2006 3:45 PM
Carl Daniel [VC++ MVP]
"DCaballero" <dcaball***@educate-global.com> wrote in message
news:1150383974.511747.125910@h76g2000cwa.googlegroups.com...
> Hi Carl, thanks for replying!
> Here are some code extracts:

You've only shown one thread here.  How are multiple threads created?  e.g.
is there a separate instance of LMSService for each thread, or is there a
single instance that starts all the threads?

If the latter, and if this snippet represents the actual code, then your
problem lies in the use of member variables to pass information to the
thread:  You've no way to know when the newly started thread has consumed
the values of sDomain, sUser and sPwd.  It could be before the first call to
new Thread() returns, or it could be after you've started all your threads.

You either need to interlock thread startup so that you're guarateed that
the new thread has consumed those values before you move on, or change the
design so that unique copies of those variables exist for each thread, e.g.
by making a Task class that has a copy of those variables and implements the
"DoTask" method that's actually run on the new thread.

-cd
Author
19 Jun 2006 3:10 PM
DCaballero
But that's exactly what I'm doing, as you can see in the code, the
sDomain, sUser and sPwd variables are private in DoTaskClass, they are
initialized with values passed to the constructor of the class, so when
each thread is started it uses its class' own variables, I thought this
way each thread would impersonate its own user. What am I doing wrong?

When you say interlock thread startup, do you mean that I start 1
thread and wait for it to impersonate before starting the next thread?
Would that be the same as impersonating the user in the main thread,
creating and starting 1 thread, impersonate the next user in the main
thread then creating and starting the next thread and so on?

David C.

Carl Daniel [VC++ MVP] wrote:
Show quote
> "DCaballero" <dcaball***@educate-global.com> wrote in message
> news:1150383974.511747.125910@h76g2000cwa.googlegroups.com...
> > Hi Carl, thanks for replying!
> > Here are some code extracts:
>
> You've only shown one thread here.  How are multiple threads created?  e.g.
> is there a separate instance of LMSService for each thread, or is there a
> single instance that starts all the threads?
>
> If the latter, and if this snippet represents the actual code, then your
> problem lies in the use of member variables to pass information to the
> thread:  You've no way to know when the newly started thread has consumed
> the values of sDomain, sUser and sPwd.  It could be before the first call to
> new Thread() returns, or it could be after you've started all your threads.
>
> You either need to interlock thread startup so that you're guarateed that
> the new thread has consumed those values before you move on, or change the
> design so that unique copies of those variables exist for each thread, e.g.
> by making a Task class that has a copy of those variables and implements the
> "DoTask" method that's actually run on the new thread.
>
> -cd

AddThis Social Bookmark Button