Accessing UNC file share with credentials?

U

Usenet User

From a .NET 1.1 app: need to access a file share on a remote server by
its UNC path, i.e., \\server\folder\subfolder\, using specific
username and password.

The problem is that the credentials are local to the remote server.
Therefore, impersonating that user when accessing the share is not
possible.

One solution is to map a network drive using the credentials, but it
seems kind of ugly and old school.

Is there any other way?

Thanks!
 
W

Willy Denoyette [MVP]

Usenet User said:
From a .NET 1.1 app: need to access a file share on a remote server by
its UNC path, i.e., \\server\folder\subfolder\, using specific
username and password.

The problem is that the credentials are local to the remote server.
Therefore, impersonating that user when accessing the share is not
possible.

One solution is to map a network drive using the credentials, but it
seems kind of ugly and old school.

Is there any other way?

Thanks!


Create a new logon session by calling LogonUser (advapi32.dll) using the
remote credentials (user, password and machinename)with logon type =
LOGON32_LOGON_NEW_CREDENTIALS (int value=9). Create a new WindowsIdentity
using the token returned from LogonUser and impersonate that new identity
when you need to access the remote resource. The new impersonated token is
used to access the remote share only , all local resources are still using
the current users token.
Please read the SDK doc's remarks on LogonUser's dwLogonType, this option
requires.

Willy.
 
U

Usenet User

Create a new logon session by calling LogonUser (advapi32.dll) using the
remote credentials (user, password and machinename)with logon type =
LOGON32_LOGON_NEW_CREDENTIALS (int value=9). Create a new WindowsIdentity
using the token returned from LogonUser and impersonate that new identity
when you need to access the remote resource. The new impersonated token is
used to access the remote share only , all local resources are still using
the current users token.
Please read the SDK doc's remarks on LogonUser's dwLogonType, this option
requires.

Willy.

Thank you for the info. BTW, I need to move the files from the remote
location to a local one, i.e., by using File.Move(...). This method,
obviously, would access both remote and local resources
simultaneously. How can it be done, then?
 
U

Usenet User

Thank you for the info. BTW, I need to move the files from the remote
location to a local one, i.e., by using File.Move(...). This method,
obviously, would access both remote and local resources
simultaneously. How can it be done, then?

Never mind. I misunderstood you at first, by I got the idea after
reading the remarks in the documentaion.

Thanks a lot!
 
L

Larry Smith

From a .NET 1.1 app: need to access a file share on a remote server by
its UNC path, i.e., \\server\folder\subfolder\, using specific
username and password.

The problem is that the credentials are local to the remote server.
Therefore, impersonating that user when accessing the share is not
possible.

One solution is to map a network drive using the credentials, but it
seems kind of ugly and old school.

Is there any other way?

I'm not sure what .NET offers natively but you can always rely on
"NetUseAdd()" in the WinAPI. This is how you would normally handle it
outside of .NET. Just change the credentials to that of the remote account
and you can then access the machine normally. Note that the domain name you
pass should be something that the remote server doesn't recognize however
(an empty string probably though I haven't done this in some years). This
ensures that authentication will occur using the remote machine's local
account which will happen anyway if the remote machine isn't part of a
domain at all.
 
U

Usenet User

Create a new logon session by calling LogonUser (advapi32.dll) using the
remote credentials (user, password and machinename)with logon type =
LOGON32_LOGON_NEW_CREDENTIALS (int value=9). Create a new WindowsIdentity
using the token returned from LogonUser and impersonate that new identity
when you need to access the remote resource. The new impersonated token is
used to access the remote share only , all local resources are still using
the current users token.
Please read the SDK doc's remarks on LogonUser's dwLogonType, this option
requires.

Willy.


It worked! Below is what I came up with. Note that in my situation
192.19.12.46 is not even a Windows machine, yet an AS/400 server.

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

namespace RemoteCredentials
{
class MainClass
{
[DllImport( "advapi32.dll", SetLastError = true )]
private static extern bool LogonUser( string
lpszUsername, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, ref
IntPtr phToken );

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private unsafe static extern int FormatMessage(int
dwFlags, ref IntPtr lpSource,
int dwMessageId, int dwLanguageId, ref String
lpBuffer, int nSize, IntPtr *arguments);

[DllImport( "kernel32.dll", CharSet = CharSet.Auto,
SetLastError = true )]
private static extern bool CloseHandle( IntPtr handle
);

[DllImport("advapi32.dll", CharSet = CharSet.Auto,
SetLastError = true)]
public extern static bool DuplicateToken( IntPtr
existingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr
duplicateTokenHandle );


// logon types
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

// logon providers
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_PROVIDER_WINNT50 = 3;
const int LOGON32_PROVIDER_WINNT40 = 2;
const int LOGON32_PROVIDER_WINNT35 = 1;

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
IntPtr token = IntPtr.Zero;
IntPtr dupToken = IntPtr.Zero;

bool isSuccess = LogonUser( "userId",
"192.19.12.46", "password",
LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_DEFAULT, ref token );
if( !isSuccess )
{
RaiseLastError();
}

isSuccess = DuplicateToken( token, 2, ref
dupToken );
if( !isSuccess )
{
RaiseLastError();
}

WindowsIdentity newIdentity = new
WindowsIdentity( dupToken );
WindowsImpersonationContext impersonatedUser =
newIdentity.Impersonate();

DirectoryInfo dirInfo = new DirectoryInfo(
@"\\192.19.12.46\sharedfolder" );
FileInfo[] files = dirInfo.GetFiles();

impersonatedUser.Undo();

foreach( FileInfo file in files )
{
Console.WriteLine( file.FullName );
}

isSuccess = CloseHandle( token );
if( !isSuccess )
{
RaiseLastError();
}
}

// GetErrorMessage formats and returns an error
message
// corresponding to the input errorCode.
public unsafe static string GetErrorMessage( int
errorCode )
{
int FORMAT_MESSAGE_ALLOCATE_BUFFER =
0x00000100;
int FORMAT_MESSAGE_IGNORE_INSERTS =
0x00000200;
int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

int messageSize = 255;
string lpMsgBuf = "";
int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;

IntPtr ptrlpSource = IntPtr.Zero;
IntPtr ptrArguments = IntPtr.Zero;

int retVal = FormatMessage(dwFlags, ref
ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &ptrArguments);
if( retVal == 0 )
{
throw new ApplicationException(
string.Format( "Failed to
format message for error code '{0}'.", errorCode ) );
}

return lpMsgBuf;
}

private static void RaiseLastError()
{
int errorCode = Marshal.GetLastWin32Error();
string errorMessage = GetErrorMessage(
errorCode );

throw new ApplicationException( errorMessage
);
}
}
}
 
W

Willy Denoyette [MVP]

Usenet User said:
Create a new logon session by calling LogonUser (advapi32.dll) using the
remote credentials (user, password and machinename)with logon type =
LOGON32_LOGON_NEW_CREDENTIALS (int value=9). Create a new WindowsIdentity
using the token returned from LogonUser and impersonate that new identity
when you need to access the remote resource. The new impersonated token is
used to access the remote share only , all local resources are still using
the current users token.
Please read the SDK doc's remarks on LogonUser's dwLogonType, this option
requires.

Willy.


It worked! Below is what I came up with. Note that in my situation
192.19.12.46 is not even a Windows machine, yet an AS/400 server.

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

namespace RemoteCredentials
{
class MainClass
{
[DllImport( "advapi32.dll", SetLastError = true )]
private static extern bool LogonUser( string
lpszUsername, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, ref
IntPtr phToken );

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private unsafe static extern int FormatMessage(int
dwFlags, ref IntPtr lpSource,
int dwMessageId, int dwLanguageId, ref String
lpBuffer, int nSize, IntPtr *arguments);

[DllImport( "kernel32.dll", CharSet = CharSet.Auto,
SetLastError = true )]
private static extern bool CloseHandle( IntPtr handle
);

[DllImport("advapi32.dll", CharSet = CharSet.Auto,
SetLastError = true)]
public extern static bool DuplicateToken( IntPtr
existingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr
duplicateTokenHandle );


// logon types
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

// logon providers
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_PROVIDER_WINNT50 = 3;
const int LOGON32_PROVIDER_WINNT40 = 2;
const int LOGON32_PROVIDER_WINNT35 = 1;

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
IntPtr token = IntPtr.Zero;
IntPtr dupToken = IntPtr.Zero;

bool isSuccess = LogonUser( "userId",
"192.19.12.46", "password",
LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_DEFAULT, ref token );
if( !isSuccess )
{
RaiseLastError();
}

isSuccess = DuplicateToken( token, 2, ref
dupToken );
if( !isSuccess )
{
RaiseLastError();
}

WindowsIdentity newIdentity = new
WindowsIdentity( dupToken );
WindowsImpersonationContext impersonatedUser =
newIdentity.Impersonate();

DirectoryInfo dirInfo = new DirectoryInfo(
@"\\192.19.12.46\sharedfolder" );
FileInfo[] files = dirInfo.GetFiles();

impersonatedUser.Undo();

foreach( FileInfo file in files )
{
Console.WriteLine( file.FullName );
}

isSuccess = CloseHandle( token );
if( !isSuccess )
{
RaiseLastError();
}
}

// GetErrorMessage formats and returns an error
message
// corresponding to the input errorCode.
public unsafe static string GetErrorMessage( int
errorCode )
{
int FORMAT_MESSAGE_ALLOCATE_BUFFER =
0x00000100;
int FORMAT_MESSAGE_IGNORE_INSERTS =
0x00000200;
int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

int messageSize = 255;
string lpMsgBuf = "";
int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;

IntPtr ptrlpSource = IntPtr.Zero;
IntPtr ptrArguments = IntPtr.Zero;

int retVal = FormatMessage(dwFlags, ref
ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &ptrArguments);
if( retVal == 0 )
{
throw new ApplicationException(
string.Format( "Failed to
format message for error code '{0}'.", errorCode ) );
}

return lpMsgBuf;
}

private static void RaiseLastError()
{
int errorCode = Marshal.GetLastWin32Error();
string errorMessage = GetErrorMessage(
errorCode );

throw new ApplicationException( errorMessage
);
}
}
}



Right, note that you don't have to call DuplicateToken, the token obtained
from LogonUser can be used to impersonate as is.

Willy.
 
B

Ben Voigt [C++ MVP]

WindowsIdentity newIdentity = new
WindowsIdentity( dupToken );
WindowsImpersonationContext impersonatedUser =
newIdentity.Impersonate();

DirectoryInfo dirInfo = new DirectoryInfo(
@"\\192.19.12.46\sharedfolder" );
FileInfo[] files = dirInfo.GetFiles();

impersonatedUser.Undo();

Very serious vulnerability here, the impersonation is not undone in case an
exception is thrown.

WindowsImpersonationContext implements IDisposable, so try:

WindowsIdentity newIdentity = new WindowsIdentity( dupToken );
using (newIdentity.Impersonate())
{
DirectoryInfo dirInfo = new
DirectoryInfo(@"\\192.19.12.46\sharedfolder" );
FileInfo[] files = dirInfo.GetFiles();
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top