?
Solved

Windows user authentication in VB.NET 3.5SP1

Posted on 2009-12-16
2
Medium Priority
?
1,143 Views
Last Modified: 2013-11-26
I have a slightly unusual problem. I am writing a VB.NET Windows Forms application that manages Windows Task Scheduler tasks. As part of my requirements, the user must be able to specify the Windows user under whose privileges the task will run. This won't necessarily be the currently connected user.

The user could be an account on the local system, or an account on the local domain. The software will need to run on Windows XP, Server 2003, Vista, Server 2008 and Windows 7.

Initially, I was using a piece of code that used SSPI to authenticate against LDAP, but that won't seem to authenticate a local user when the system is attached to a domain and won't authenticate a domain user (even the currently logged in user) if you're disconnected from the network.

So, I switched to a function which used LogonUser to authenticate the user which worked great until I tried it on Vista and found that the users need a specific permission before they can be authenticated using this method.

I have attached my existing functions which are modifications of code I found on the internet.

I appreciate any help I can get.
''' <summary>
    ''' Validates whether the passed Windows user Id and password are valid.
    ''' </summary>
    ''' <param name="szUserName">The Windows user name with domain prepended if necessary.</param>
    ''' <param name="szPassword">The user's Windows password.</param>
    ''' <param name="szPasswordVerify">The user's Windows password again.</param>
    ''' <returns>True if the user's ID and password are correct.</returns>
    ''' <remarks></remarks>
    Public Function ValidateUserIdPassword(ByVal szUserName As String, ByVal szPassword As String, ByVal szPasswordVerify As String) As Boolean
        Dim bRetVal As Boolean = False
        Console.WriteLine("Validating user {0}", szUserName)
        If szPassword.Equals(szPasswordVerify) Then
            Console.WriteLine("Passwords match.")
            AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal) 
            ' Retrieve the computer name
            Dim machName As String = Environment.MachineName
            Dim MyNamespace As IADsOpenDSObject
            Dim oUserValidation As Object
            Dim DN As String = "LDAP://rootDSE"
            Dim ADS_AUTHENTICATION_SECURE As New ADS_AUTHENTICATION_ENUM
            MyNamespace = GetObject("LDAP:")
            'For authentication, pass in a variable for the user name and password that you wish to use for
            'authentication purposes. It is recommended that you use the ADS_AUTHENTICATION_SECURE flag for
            'security reasons. 
            Try
                oUserValidation = MyNamespace.OpenDSObject(DN, szUserName, szPassword, ADS_AUTHENTICATION_SECURE)
                bRetVal = True
            Catch ex As Exception
                ' Do nothing
                Console.WriteLine("Error {0}", ex.Message)
            End Try
        End If
\        Return bRetVal
    End Function 
    ''' <summary>
    ''' Validates a Windows user Id, domain and password combination.
    ''' </summary>
    ''' <param name="szUserName">The Windows user name. This can be in the form of username, domain\username, or username@domain. If the domain is specified like this, leave the szUserDomain parameter blank.</param>
    ''' <param name="szUserDomain">The Windows machine name or domain. If the domain is specified as part of the user name, pass an empty string in this parameter.</param>
    ''' <param name="szPassword">The user's password.</param>
    ''' <param name="szPasswordVerify">A password validation check.</param>
    ''' <returns>True if all the information is correct. False if one or more items were incorrect.</returns>
    ''' <remarks>Uses the Windows API LogonUser function to check the security context. The context is restore before the function exits.</remarks>
    Public Function ValidateUserIdPassword(ByVal szUserName As String, ByVal szUserDomain As String, ByVal szPassword As String, ByVal szPasswordVerify As String) As Boolean
        ' Set default return value.
        Dim bRetVal As Boolean = False
        ' Compare passwords.
        If szPassword.Equals(szPasswordVerify) Then
            ' Password is same in password and password verify variables.
            If szUserDomain = "" Then
                If szUserName.Contains("\") Then
                    ' Pull the domain out of the user name.
                    szUserDomain = szUserName.Substring(0, szUserName.IndexOf("\"))
                    ' Set user name to just user name.
                    szUserName = szUserName.Substring(szUserName.IndexOf("\") + 1)
                ElseIf szUserName.Contains("@") Then
                    ' User Name is in form of "user@domain"
                    ' Pull the domain out of the user name.
                    szUserDomain = szUserName.Substring(szUserName.IndexOf("@") + 1)
                    ' Set user name to just user name.
                    szUserName = szUserName.Substring(0, szUserName.IndexOf("@"))
                Else
                    ' A "." refers to the local system.
                    szUserDomain = "."
                End If 
            End If
            Console.WriteLine("Validating user {0}, Domain {1}", szUserName, szUserDomain) 
            Dim lphToken As IntPtr = IntPtr.Zero 
            ' Attempt to log in to the machine/domain. Function returns True or False depending on the user Id, password and domain work.
            bRetVal = LogonUser(szUserName, szUserDomain, szPassword, LogonType.LOGON32_LOGON_INTERACTIVE, LogonProvider.LOGON32_PROVIDER_DEFAULT, lphToken) 
            ' Close the handle received.
            CloseHandle(lphToken)
            ' Revert security to the currently logged in user.
            RevertToSelf()
        End If 
        Return bRetVal
    End Function

Open in new window

0
Comment
Question by:wjpoconnor
2 Comments
 
LVL 10

Expert Comment

by:anv
ID: 26070784
dont seem to understand the problem properly.
Is it that windows scheduler task need to be created?
or is it that LogonUser  is not working?
0
 

Accepted Solution

by:
wjpoconnor earned 0 total points
ID: 26072699
I've come up with a solution. The task scheduler part is immaterial. It was just explaining why I need to get the user credentials and why I needed to validate they were correct. LogonUser, for my purposes, wasn't effective on Vista and newer operating systems.

A co-worker of mine found a potential solution at http://social.msdn.microsoft.com/Forums/fi-FI/netfxnetcom/thread/2a9dd787-74dd-4e15-b866-1502753c30f9 and I modified it to the code which I have attached. I think this should solve my issues, at least until I run into the next problem.
    Public Function PlainTextValidateUser(ByVal szUserName As String, ByVal szPassword As String) As Boolean
        Dim result As Boolean = False
        Dim szUserDomain As String = ""
        Dim myLDAPPath As String

        ' Determine what the domain name should be.
        If szUserName.Contains("\") Then
            ' Pull the domain out of the user name.
            szUserDomain = szUserName.Substring(0, szUserName.IndexOf("\"))
            ' Set user name to just user name.
            szUserName = szUserName.Substring(szUserName.IndexOf("\") + 1)
        ElseIf szUserName.Contains("@") Then
            ' User Name is in form of "user@domain"
            ' Pull the domain out of the user name.
            szUserDomain = szUserName.Substring(szUserName.IndexOf("@") + 1)
            ' Set user name to just user name.
            szUserName = szUserName.Substring(0, szUserName.IndexOf("@"))
        Else
            ' A "." refers to the local system.
            szUserDomain = Environment.MachineName
            szUserName = szUserName.Substring(szUserName.IndexOf("\") + 1)
        End If

        ' Determine which entry it will need to be.
        If String.Compare(szUserDomain, Environment.MachineName, True) = 0 Then
            myLDAPPath = "WinNT://" & szUserDomain
        Else
            myLDAPPath = "LDAP://" & szUserDomain
        End If

        Try
            Dim entry As DirectoryEntry = New DirectoryEntry(myLDAPPath, szUserName, szPassword)
            Dim nativeObject As Object = entry.NativeObject
            result = True 'no exception thrown, user must exist
            nativeObject = Nothing  'be sure and clean up these object as this service could be used many times
            entry = Nothing
        Catch ex As Exception
            result = False  'exception thrown - no user with that name/pwd combination
        End Try
        Return result
    End Function

Open in new window

0

Featured Post

Veeam and MySQL: How to Perform Backup & Recovery

MySQL and the MariaDB variant are among the most used databases in Linux environments, and many critical applications support their data on them. Watch this recorded webinar to find out how Veeam Backup & Replication allows you to get consistent backups of MySQL databases.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
Simulator games are perfect for generating sample realistic data streams, especially for learning data analysis. It is even useful for demoing offerings such as Azure stream analytics, PowerBI etc.
We’ve all felt that sense of false security before—locking down external access to a database or component and feeling like we’ve done all we need to do to secure company data. But that feeling is fleeting. Attacks these days can happen in many w…
Loops Section Overview
Suggested Courses

807 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question