4

I need to be able to programmatically authenticate when trying to read and write files on a remote computer in a non-domain environment.

When you type a command into the Windows RUN prompt that is similar to \\targetComputer\C$\targetFolder or \\targetComputer\admin$, where the targetComputer is NOT on a domain, you will be prompted to enter a username and password. Once you enter the username and password, you have full access to the remote folder.

How can I accomplish this authentication programmatically in C#?

I've tried..

--Impersonation, but it appears to only work in a domain environment.

--CMDKEY.exe, but it also seems to only work in a domain environment.

There must be a way to do this, but I have searched high and low with no luck so far. Maybe I'm just looking for the wrong thing? I'm sure I'm not the first to have this question. Any help would be greatly appreciated.

Thanks!

EDIT :

I think I just found a different SO posting that answers my question: Accessing a Shared File (UNC) From a Remote, Non-Trusted Domain With Credentials

I will work with that for now and see where it gets me.

Thanks!

Community
  • 1
  • 1
blitz_jones
  • 1,048
  • 2
  • 10
  • 22
  • I think I just found a different SO posting that answers my question: Accessing a Shared File (UNC) From a Remote, Non-Trusted Domain With Credentials I will work with that for now and see where it gets me. Thanks! – blitz_jones Jun 20 '13 at 20:32

1 Answers1

13

Impersonation works with Peer/LAN network as well. I got your typical home network with some machines on default "Workgroup" and some on a named one if I remembered doing it on the install.

Here is the code I use from my IIS server app to access files on my other computer (without having to have the same user and password on both machines involved, copied from somewhere and modified for my use):

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.ComponentModel;

/// <summary>
/// Class to impersonate another user. Requires user, pass and domain/computername
/// All code run after impersonationuser has been run will run as this user.
/// Remember to Dispose() afterwards.
/// </summary>
public class ImpersonateUser:IDisposable {

    private WindowsImpersonationContext LastContext = null;
    private IntPtr LastUserHandle = IntPtr.Zero;

    #region User Impersonation api
    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool ImpersonateLoggedOnUser(int Token);

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

    [DllImport("kernel32.dll")]
    public static extern Boolean CloseHandle(IntPtr hObject);

    public const int LOGON32_PROVIDER_DEFAULT = 0;
    public const int LOGON32_PROVIDER_WINNT35 = 1;
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_LOGON_NETWORK = 3;
    public const int LOGON32_LOGON_BATCH = 4;
    public const int LOGON32_LOGON_SERVICE = 5;
    public const int LOGON32_LOGON_UNLOCK = 7;
    public const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;// Win2K or higher
    public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;// Win2K or higher
    #endregion

    public ImpersonateUser(string username, string domainOrComputerName, string password, int nm = LOGON32_LOGON_NETWORK) {

        IntPtr userToken = IntPtr.Zero;
        IntPtr userTokenDuplication = IntPtr.Zero;

        bool loggedOn = false;

        if (domainOrComputerName == null) domainOrComputerName = Environment.UserDomainName;

        if (domainOrComputerName.ToLower() == "nt authority") {
            loggedOn = LogonUser(username, domainOrComputerName, password, LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT, out userToken);
        } else {
            loggedOn = LogonUser(username, domainOrComputerName, password, nm, LOGON32_PROVIDER_DEFAULT, out userToken);
        }

        WindowsImpersonationContext _impersonationContext = null;
        if (loggedOn) {
            try {
                // Create a duplication of the usertoken, this is a solution
                // for the known bug that is published under KB article Q319615.
                if (DuplicateToken(userToken, 2, ref userTokenDuplication)) {
                    // Create windows identity from the token and impersonate the user.
                    WindowsIdentity identity = new WindowsIdentity(userTokenDuplication);
                    _impersonationContext = identity.Impersonate();
                } else {
                    // Token duplication failed!
                    // Use the default ctor overload
                    // that will use Mashal.GetLastWin32Error();
                    // to create the exceptions details.
                    throw new Win32Exception();
                }
            } finally {
                // Close usertoken handle duplication when created.
                if (!userTokenDuplication.Equals(IntPtr.Zero)) {
                    // Closes the handle of the user.
                    CloseHandle(userTokenDuplication);
                    userTokenDuplication = IntPtr.Zero;
                }

                // Close usertoken handle when created.
                if (!userToken.Equals(IntPtr.Zero)) {
                    // Closes the handle of the user.
                    CloseHandle(userToken);
                    userToken = IntPtr.Zero;
                }
            }
        } else {
            // Logon failed!
            // Use the default ctor overload that 
            // will use Mashal.GetLastWin32Error();
            // to create the exceptions details.
            throw new Win32Exception();
        }

        if (LastContext == null) LastContext = _impersonationContext;
    }

    public void Dispose() {
        LastContext.Undo();
        LastContext.Dispose();
    }
}

The specific code I found out worked after a bit of trying was this:

using (var impersonation = new ImpersonateUser("OtherMachineUser", "OtherMachineName", "Password", LOGON32_LOGON_NEW_CREDENTIALS))
    {
        var files = System.IO.Directory.GetFiles("\\OtherMachineName\fileshare");
    }
Wolf5
  • 16,600
  • 12
  • 59
  • 58
  • I was able to use the following code to solve my problem: http://stackoverflow.com/questions/659013/accessing-a-shared-file-unc-from-a-remote-non-trusted-domain-with-credentials?rq=1. However, I'm curious to test your code later (I think I actually might have... feel like I saw that code on SO before I posted this question) and see how it compares to my original impersonation code. First glance of your code is that it looks just like the code I was using previously with only success in domain environments. I will look at it more closely later. Thanks. – blitz_jones Oct 06 '13 at 22:27
  • 2
    The key is: LOGON32_LOGON_NEW_CREDENTIALS. I tried all the different choices and LOGON32_LOGON_NEW_CREDENTIALS was the one that worked. The rest of the code is generic. – Wolf5 Oct 08 '13 at 07:20
  • Very interesting. I will definitely try it out and report back. Thanks! – blitz_jones Oct 08 '13 at 19:08
  • This code worked for me as well, though I was solving a slightly different problem. My goal was to access a file on a network folder that used a different domain than my local machine. Amazingly, despite the dll imports, it worked right out of the box! – S. Dixon Sep 22 '14 at 23:12
  • I had forgotten to report back but just wanted to say that this code seems to work great. Thanks again! – blitz_jones Nov 14 '14 at 17:38
  • Can a non shared file/folder be access using this method? – Pabitra Dash Jun 22 '15 at 09:16
  • It's just impersonation so yes it can be used locally to access files under different credentials. It can not access files on another computer that has not been shared in anyway. – Wolf5 Jun 22 '15 at 14:06
  • Once I accessed the files by using above code . After then how i will remove access of shared folder? . Because during access shared folder in Window it will stored user Id and Password in Cache. So is there any need to remove that Cache ? – Karthick Rajan Sep 22 '16 at 10:26
  • I am not aware of any caching mechanism with this method. I would assume no caching is happening. Caching happens when you get a popup in windows and enter credentials and check "Remember my credentials". Just test running the code and check the Credentials Manager to see if any new entries has been added. – Wolf5 Sep 22 '16 at 20:57
  • can you provide a valid filepath of the remote machine? my machine name is Server and I need to access D drive on it, I am getting an error when I try: var files = System.IO.Directory.GetFiles("\\Server\\D:"); – Yahya Hussein Dec 21 '16 at 07:56
  • ummm. That is not a valid network path. Go share a folder or drive on the server, open it via Explorer and copy the path and use it in your program. Like \\server\myshare or \\server\d (IF u shared the d: disk as "d") – Wolf5 Dec 23 '16 at 16:47
  • When i access remote pc shared directory an error came "Device is not ready" – Rizwan Ali Sabir Jul 21 '17 at 09:46