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?
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.
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With