LogonUser API Impersonation to SQL Server 
Author Message
 LogonUser API Impersonation to SQL Server

Hello all,

I'm going nuts.  I'm trying to figure out how to impersonate a user to
access a SQL Server.  Here's my configuration

Client Machine -- ClientA
SQL Server Machine -- SQLB
Domain -- MyDomain
User -- DomainUserA
User -- DomainUserB

Class library on client -- MyDataProvider
Forms app on client -- myDe{*filter*}

Ok, I'm running myDe{*filter*} in debug mode on ClientA with a reference to
MyDataProvider while logged on as myself (DomainUserA).  
In myDataProvider, before I try and make a connection to the database I
call the LogonUser API to log on as DomainUserB who has a User Account
set up in SQL Server that maps to the DomainUserB account with access to
the database.  myConnection string is as follows, "Integrated
Security=SSPI;Initial Catalog=myDataBase;Data Source=SQLB".  When I add
a watch to the WindowsIdentity.GetCurrent(), before I call LogonUser,
the WindowsIdentity.GetCurrent().Name = "DomainUserA" (who also happens
to have access to the database, so I know it's not permissions.)  If I
skip over the code to impersonate the identity of DomainUserB, I can
connect just fine.  When I call logonUser, get my token back, set the
context to impersonate, the watch for WindowsIdentity.GetCurrent().Name
changes to "DomainUserB" successfully.  When I try and open the
connection, I get the following error, "Login failed for user 'NT
AUTHORITY\ANONYMOUS LOGON'."  Both DomainUserA and DomainUserB have
rights to the database, but for some reason it's not trying to connect
as either one.  I KNOW my permissions are set wrong for DomainUserB on
the client box, but I just don't know how to make them right?!?  Any
ideas?  Here's my sample code too.

<DllImport("advapi32.dll")> _
        Public Shared Function LogonUser(ByVal lpszUsername As String,
ByVal lpszDomain As String, ByVal lpszPassword As String, ByVal
dwLogonType As Integer, ByVal dwLogonProvider As Integer, ByRef
ByRefphToken As IntPtr) As Boolean
        End Function

        <DllImport("Kernel32.dll")> _
        Public Shared Function GetLastError() As Integer
        End Function

Private Sub LogonMyUser(ByVal strDomain as string, ByVal strUserName as
string, ByVal strPassword as String)
Dim token1 As New IntPtr(0)
        'Get the user token for the specified user, machine, and
password using the unmanaged LogonUser method.
        Dim loggedOn As Boolean = LogonUser(strUserName, strDomain,
strPassword, 3, 0, token1)
        If loggedOn And token1.ToInt32 <> 0 Then
            Dim myNewIdentity As New WindowsIdentity(token1)
            myNewContext = myNewIdentity.Impersonate()
        Else
            'Call GetLastError to try to determine why logon failed if
it did not succeed.
            Dim ret As Integer = GetLastError()
        End If
End Sub

Any ideas would be greatly appreciated.  

thx,
ctillman

*** Sent via Developersdex http://www.*-*-*.com/ ***
Don't just participate in USENET...get rewarded for it!



Wed, 13 Jul 2005 02:36:34 GMT  
 LogonUser API Impersonation to SQL Server
A lot of this code is taken from the sample that comes in the documentation
for the WindowsIdentity.Impersonate, unfortunately it's not a very good
sample and has several errors. The first of which is that you cannot
reliably use GetLastError from Managed code since the runtime calls various
APIs behind the scenes. You should use
System.Runtime.InteropServices.Marshal.GetLastWin32Error in its place. Also,
in order to get the last error you must add the SetLastError:=True property
to your DllImport attribute. As well if loggedOn is False there isn't much
point in setting ret to the last error number if you're not going to throw
an exception out to the caller to tell them what happened.

Another failing of the sample is that it isn't very clear what kind of
impersonation can be done this way. The sample uses LOGON32_LOGON_NETWORK(3)
for the logon type but this returns an impersonation token that is very
limited (this is the type of logon token created for someone accessing a
computer from across the network). But this token has no network credentials
therefore when you try to use it over the network the remote end is seeing
an attempt at an anonymous logon. Only primary tokens are created with
network credentials. These are created using a dwLogonType parameter of
LOGON32_LOGON_INTERACTIVE(2), LOGON32_LOGON_BATCH(4) or
LOGON32_LOGON_SERVICE(5), of course the account will need the appropriate
privilege to use these. So the sample applies to accessing local resources
only.

So you need to get a primary token but when you pass that to
WindowsIdentity.Impersonate you'll now get an exception saying that you were
unable to impersonate. This is because the Impersonate method calls
SetThreadToken behind the scenes and this API call will accept only an
impersonation token, not a primary token. The framework is not really set up
for this kind of impersonation it is more oriented toward a network server
program like ASP.NET using Windows authentication and then impersonation to
access the local file system or DB.

To get around all this you can either call DuplicateToken requesting an
impersonation token and then use that token in the Impersonate call. Or,
more likely, bypass the framework entirely by using the primary token to
call the ImpersonateLoggedOnUser API, do your stuff and then call the
RevertToSelf API.

By the way be very careful of the security aspects of this, a user that can
call LogonUser is fairly powerful and you should try to limit that power to
the LocalSystem account only.


Quote:

> Hello all,

> I'm going nuts.  I'm trying to figure out how to impersonate a user to
> access a SQL Server.  Here's my configuration

> Client Machine -- ClientA
> SQL Server Machine -- SQLB
> Domain -- MyDomain
> User -- DomainUserA
> User -- DomainUserB

> Class library on client -- MyDataProvider
> Forms app on client -- myDe{*filter*}

> Ok, I'm running myDe{*filter*} in debug mode on ClientA with a reference to
> MyDataProvider while logged on as myself (DomainUserA).
> In myDataProvider, before I try and make a connection to the database I
> call the LogonUser API to log on as DomainUserB who has a User Account
> set up in SQL Server that maps to the DomainUserB account with access to
> the database.  myConnection string is as follows, "Integrated
> Security=SSPI;Initial Catalog=myDataBase;Data Source=SQLB".  When I add
> a watch to the WindowsIdentity.GetCurrent(), before I call LogonUser,
> the WindowsIdentity.GetCurrent().Name = "DomainUserA" (who also happens
> to have access to the database, so I know it's not permissions.)  If I
> skip over the code to impersonate the identity of DomainUserB, I can
> connect just fine.  When I call logonUser, get my token back, set the
> context to impersonate, the watch for WindowsIdentity.GetCurrent().Name
> changes to "DomainUserB" successfully.  When I try and open the
> connection, I get the following error, "Login failed for user 'NT
> AUTHORITY\ANONYMOUS LOGON'."  Both DomainUserA and DomainUserB have
> rights to the database, but for some reason it's not trying to connect
> as either one.  I KNOW my permissions are set wrong for DomainUserB on
> the client box, but I just don't know how to make them right?!?  Any
> ideas?  Here's my sample code too.

> <DllImport("advapi32.dll")> _
>         Public Shared Function LogonUser(ByVal lpszUsername As String,
> ByVal lpszDomain As String, ByVal lpszPassword As String, ByVal
> dwLogonType As Integer, ByVal dwLogonProvider As Integer, ByRef
> ByRefphToken As IntPtr) As Boolean
>         End Function

>         <DllImport("Kernel32.dll")> _
>         Public Shared Function GetLastError() As Integer
>         End Function

> Private Sub LogonMyUser(ByVal strDomain as string, ByVal strUserName as
> string, ByVal strPassword as String)
> Dim token1 As New IntPtr(0)
>         'Get the user token for the specified user, machine, and
> password using the unmanaged LogonUser method.
>         Dim loggedOn As Boolean = LogonUser(strUserName, strDomain,
> strPassword, 3, 0, token1)
>         If loggedOn And token1.ToInt32 <> 0 Then
>             Dim myNewIdentity As New WindowsIdentity(token1)
>             myNewContext = myNewIdentity.Impersonate()
>         Else
>             'Call GetLastError to try to determine why logon failed if
> it did not succeed.
>             Dim ret As Integer = GetLastError()
>         End If
> End Sub

> Any ideas would be greatly appreciated.

> thx,
> ctillman

> *** Sent via Developersdex http://www.*-*-*.com/ ***
> Don't just participate in USENET...get rewarded for it!



Wed, 13 Jul 2005 05:49:25 GMT  
 
 [ 2 post ] 

 Relevant Pages 

1. API Impersonation calls

2. Createprocesswithlogon and LogonUser APIs

3. LogonUser API function - question

4. Trouble with LogonUser API

5. LogonUser API problem

6. Repost: Help with LogonUser API call needed

7. LogonUser API help

8. LogonUser API call help needed

9. LogonUser API

10. Pls Help! LogonUser API Problem

11. Using LogonUSer API fails

12. Cant get LogonUser() to work in VB (Win32 API)

 

 
Powered by phpBB® Forum Software