Can you explain why DirectoryInfo.GetFiles produces this IOException?
Asked Answered
P

5

17

I have a WinForms client-server app running on a Novell network that produces the following error when connecting to the lone Windows 2003 Server on the network:

TYPE: System.IO.IOException
MSG: Logon failure: unknown user name or bad password.

SOURCE: mscorlib
SITE: WinIOError

  at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
  at System.IO.Directory.InternalGetFileDirectoryNames(String path,
    String userPathOriginal, String searchPattern, Boolean includeFiles, 
    Boolean includeDirs, SearchOption searchOption)
  at System.IO.DirectoryInfo.GetFiles(String searchPattern, 
    SearchOption searchOption)
  at System.IO.DirectoryInfo.GetFiles(String searchPattern)
  at Ceoimage.Basecamp.DocumentServers.ClientAccessServer.SendQueuedFiles(
    Int32 queueId, Int32 userId, IDocQueueFile[] queueFiles)
  at Ceoimage.Basecamp.ScanDocuments.DataModule.CommitDocumentToQueue(
    QueuedDocumentModelWithCollections doc, IDocQueueFile[] files)

The customer's network admin manages the Windows Server connection by manually synchronizing the workstation username and password with a local user on the server. The odd thing about the error is that the user can write to the server both before and after the error, all without explicitly logging on.

Can you explain why the error occurs and offer a solution?

Phantasy answered 24/2, 2009 at 18:35 Comment(3)
There may be no implicit trust between Novell and the Windows workgroup or domain.Photic
Is this "implicit trust" something that can be configured?Phantasy
Can you post the code segment that connects to the server?Cleopatracleopatre
T
50

I have this same problem when trying to access the file system of a windows server in a different domain. The problem is that the user account that the program is running under does not have access to the remote server. Windows does extra work behind the scenes to make it look seamless when you use Windows Explorer because it guesses that your remote credentials will match your local credentials.

If you map a drive locally to the remote server, then use the locally mapped drive in your code, you shouldn't have the problem. If you can't map a drive, but you can hard code the credentials to use for the remote server, then you can use this code:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace Company.Security
{
    public class ImpersonateUser : IDisposable
    {
        [DllImport("advapi32.dll", SetLastError=true)]
        private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);

        [DllImport( "kernel32", SetLastError = true )]
        private static extern bool CloseHandle(IntPtr hObject);

        private IntPtr userHandle = IntPtr.Zero;
        private WindowsImpersonationContext impersonationContext;

        public ImpersonateUser( string user, string domain, string password )
        {
            if ( ! string.IsNullOrEmpty( user ) )
            {
                // Call LogonUser to get a token for the user
                bool loggedOn = LogonUser( user, domain, password,
                    9 /*(int)LogonType.LOGON32_LOGON_NEW_CREDENTIALS*/,
                    3 /*(int)LogonProvider.LOGON32_PROVIDER_WINNT50*/,
                    out userHandle );
                if ( !loggedOn )
                    throw new Win32Exception( Marshal.GetLastWin32Error() );

                // Begin impersonating the user
                impersonationContext = WindowsIdentity.Impersonate( userHandle );
            }
        }

        public void Dispose()
        {
            if ( userHandle != IntPtr.Zero )
                CloseHandle( userHandle );
            if ( impersonationContext != null )
                impersonationContext.Undo();
        }
    }
}

Then you can access the remote server by doing this:

using ( new ImpersonateUser( "UserID", "Domain", "Password" ) )
{
    // Any IO code within this block will be able to access the remote server.
}
Tabling answered 26/2, 2009 at 21:0 Comment(7)
I have a similar method for impersonating a user, but it seems to only work when the user is a local admin. Do you find this to be the case?Phantasy
Which user needs to be a local admin? Your remote user might need to be an admin in order to access the share.Tabling
I'm saying you need to have some kind of "can impersonate user" permission on the local machine, which most standard users don't.Phantasy
Also, my problem is intermittent. One moment, the user is accessing the server; the next moment, the user gets this IOException; the next moment, the user is accessing the server.Phantasy
I've made almost same class as ImpersonateUser some time ago, which also was implementing IDisposable to use it inside using block. Happy to see that I'm not the only one.Hards
No offense to David, but this is not the answer to my question. SO auto-answered with the question that got the highest number of votes. My situation is closer to the one described by Alexandrul.Phantasy
Worked for me!!Carissacarita
E
3

For the VB.Net developers (like me) here's the VB.Net version:

Imports System
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Security.Principal

Namespace Company.Security
    Public Class ImpersonateUser
        Implements IDisposable

        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function LogonUser(ByVal lpszUsername As String, ByVal lpszDomain As String, ByVal lpszPassword As String, ByVal dwLogonType As Integer, ByVal dwLogonProvider As Integer, ByRef phToken As IntPtr) As Integer
        End Function

        <DllImport("kernel32", SetLastError:=True)> _
        Private Shared Function CloseHandle(ByVal hObject As IntPtr) As Boolean
        End Function

        Private userHandle As IntPtr = IntPtr.Zero
        Private impersonationContext As WindowsImpersonationContext

        Public Sub New(ByVal user As String, ByVal domain As String, ByVal password As String)
            If Not String.IsNullOrEmpty(user) Then
                Dim loggedOn As Integer = LogonUser(user, domain, password, 9, 3, userHandle)
                If Not loggedOn = 1 Then
                    Throw New Win32Exception(Marshal.GetLastWin32Error())
                End If
                impersonationContext = WindowsIdentity.Impersonate(userHandle)
            End If
        End Sub

        Public Sub Dispose() Implements System.IDisposable.Dispose
            If userHandle <> IntPtr.Zero Then
                CloseHandle(userHandle)
            End If
            If impersonationContext IsNot Nothing Then
                impersonationContext.Undo()
            End If
        End Sub

    End Class
End Namespace

And use it like:

using New ImpersonateUser( "UserID", "Domain", "Password" ) 
    ' ... your code here
End Using
Enow answered 6/4, 2011 at 7:49 Comment(0)
C
1

I think you should try to reproduce the problem, and than use a packet monitor to see the network traffic and look at the difference between the failure situation and the success situation.

Then write a application which uses the raw api's from windows (P/Invokes) to reproduce your failure situation and try to find which parameters cause the error to occur. If your able to solve the problem than it's just the matter of finding how to get the components to do the thing you want.

Other directions you could look at (after you can stably reproduce the problem):

  • Use Process Monitor to log all api calls and see where the error comes from.
  • Try it on a clean VM/Machine and try to reproduce it there
  • Disable the virus scanner
  • Update the novell client
Concupiscence answered 5/3, 2009 at 8:58 Comment(0)
D
1

IMHO, it seems to be some kind of side effect of refreshing an expired authentication token (or something like that).

I my case, as an Active Directory user having internet access through a proxy (squid), I am browsing without problems until I get (at random intervals) an error about lack of credentials, which is solved by a page refresh in the browser, then everything is working fine until the next error.

Diner answered 5/3, 2009 at 9:19 Comment(0)
D
0

There is a Microsoft example available at this address: https://msdn.microsoft.com/en-us/library/system.security.principal.windowsimpersonationcontext(v=vs.110).aspx

But it does say in their example: "On Windows Vista and later this sample must be run as an administrator."

So this solution is good only if the user running the code is admin on most platforms.

Dinghy answered 27/2, 2018 at 14:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.