views:

371

answers:

4

I have a C# console application that needs to read a shared file on a machine in another domain. When the application tries to access the file an exception occurs as the local user does not have permission to access the shared resource.

Currently I overcome this problem manually by open the shared folder from the run and put the username and password into the windows authentication dialog then run the application.

How can I do it programmatically?

A: 

From memory you'll need to use a Windows API call and login as a user on the other domain. See this link for an example.

Another idea could be to use the RunAs command line argument to read the file and save it into a file on your local domain/server.

Kane
I can not use RunAs for two thingsfirst thing the local machine does not contain this user and does not belong to to any domain, second thing is the file is very huge to copy it locally
Ahmed Said
+2  A: 

a) p/invoke LogonUser with LOGON32_LOGON_NEW_CREDENTIALS and create a new WindowsIdentity with the new token, then use normal file access.

b) p/invoke WNetAddConnection3. Be advised that this makes your remote share accessible to every other process on your machine.

c) WMI via System.Management and CIM_DataFile; you won't even need p/invoke. System.Management lets you specify credentials for remote machine.

Anton Tykhyy
for "a" the msdn says"You cannot use LogonUser to log on to a remote computer"
Ahmed Said
actually you can — edited
Anton Tykhyy
Excellent! I was looking to authenticate to a remote machine in a different domain w/ no trust and the solution a) worked for me. And it looks like the token returned by LogonUser with LOGON32_LOGON_NEW_CREDENTIALS is a primary token since I could just do WindowsIdentity.Impersonate(token)
Jiho Han
A: 

I used the point "a" as "Anton" suggested, I developed two versions for one class, first one using Win32 APIs and second uses WindowsIdentity class

Version 1:

class UserImpersonation:IDisposable
    {       
        [DllImport("advapi32.dll")]
            public static extern int LogonUser(String lpszUserName,
                String lpszDomain,
                String lpszPassword,
                int dwLogonType,
                int dwLogonProvider,
                ref IntPtr phToken);

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

            [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            public static extern bool RevertToSelf();

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

            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int LOGON32_LOGON_INTERACTIVE = 2;

            WindowsImpersonationContext wic;
            string _userName;
            string _domain;
            string _passWord;
            public UserImpersonation(string userName, string domain, string passWord)
            {
                _userName = userName;
                _domain = domain;
                _passWord = passWord;
            }
            public bool ImpersonateValidUser()
            {
                WindowsIdentity wi;
                IntPtr token = IntPtr.Zero;
                IntPtr tokenDuplicate = IntPtr.Zero;

                if (RevertToSelf())
                {
                    if (LogonUser(_userName, _domain, _passWord, LOGON32_LOGON_INTERACTIVE,
                        LOGON32_PROVIDER_DEFAULT, ref token) != 0)
                    {
                        if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                        {
                            wi = new WindowsIdentity(tokenDuplicate);
                            wic = wi.Impersonate();
                            if (wic != null)
                            {
                                CloseHandle(token);
                                CloseHandle(tokenDuplicate);
                                return true;
                            }
                        }
                    }
                }
                if (token != IntPtr.Zero)
                    CloseHandle(token);
                if (tokenDuplicate != IntPtr.Zero)
                    CloseHandle(tokenDuplicate);
                return false;
            }

        #region IDisposable Members
            public void Dispose()
            {
                if(wic != null)
                 wic.Dispose();
                RevertToSelf();

            }
            #endregion
    }

Version2 (from MSDN with small changes)

class UserImpersonation2:IDisposable
    {
        [DllImport("advapi32.dll")]
        public static extern bool LogonUser(String lpszUserName,
            String lpszDomain,
            String lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            ref IntPtr phToken);

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

        WindowsImpersonationContext wic;
        IntPtr tokenHandle;
        string _userName;
        string _domain;
        string _passWord;

        public UserImpersonation2(string userName, string domain, string passWord)
        {
            _userName = userName;
            _domain = domain;
            _passWord = passWord;
        }

        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_LOGON_INTERACTIVE = 2;

        public bool ImpersonateValidUser()
        {
            bool returnValue = LogonUser(_userName, _domain, _passWord,
                    LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                    ref tokenHandle);

            Console.WriteLine("LogonUser called.");

            if (false == returnValue)
            {
                int ret = Marshal.GetLastWin32Error();
                Console.WriteLine("LogonUser failed with error code : {0}", ret);
                return false;
            }

            Console.WriteLine("Did LogonUser Succeed? " + (returnValue ? "Yes" : "No"));
            Console.WriteLine("Value of Windows NT token: " + tokenHandle);

            // Check the identity.
            Console.WriteLine("Before impersonation: "
                + WindowsIdentity.GetCurrent().Name);
            // Use the token handle returned by LogonUser.
            WindowsIdentity newId = new WindowsIdentity(tokenHandle);
            wic = newId.Impersonate();

            // Check the identity.
            Console.WriteLine("After impersonation: "
                + WindowsIdentity.GetCurrent().Name);
            return true;
        }
        #region IDisposable Members
        public void Dispose()
        {
            if(wic!=null)
                wic.Undo();
            if (tokenHandle != IntPtr.Zero)
                CloseHandle(tokenHandle);

        }
        #endregion
    }

How to use (both are the same)

            const string file = @"\\machine\test\file.txt";

            using (UserImpersonation user = new UserImpersonation("user", "domain", "password"))
            {
                if (user.ImpersonateValidUser())
                {
                    StreamReader reader = new StreamReader(file);
                    Console.WriteLine(reader.ReadToEnd());
                    reader.Close();
                }
            }
Ahmed Said
This won't work if the user is a local user on the remote machine, or if the user is a domain user, and the local machine is not in the domain.
Anton Tykhyy
A: 

Does this hack also work for windows background services?

Willem Luijk
it should be question or a comment not an answer!!
Ahmed Said