views:

340

answers:

1

This .NET API works OK if I'm trying to open the Registry in a machine that's in the same domain as I am (and my logged-on user has admin rights on the target machine).

It gets tricky if it's an out-of-domain machine with a different, local administrative user (of whom I do have the password).

I tried to use WNetUseConnection() (which has served me well in the past in situations where what I wanted was to read a remote disk file) prior to calling OpenRemoteBaseKey(), but no dice -- I get an access denied exception.

Clearly, I must pass credentials some other way, but how?

+8  A: 

What I've used successfully to access files on a computer is the following code:

    #region imports 
        [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, 
        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); 
        #endregion 
        #region logon consts 
        // 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; 
        #endregion 

And then for signing in part, just use:

        IntPtr token = IntPtr.Zero; 

        bool isSuccess = LogonUser("username", "domain", "password", 
        LOGON32_LOGON_NEW_CREDENTIALS, 
        LOGON32_PROVIDER_DEFAULT, ref token); 
        using (WindowsImpersonationContext person = new WindowsIdentity(token).Impersonate()) 
        { 
        //do your thing 
         person.Undo(); 
        } 

As you might see, "Undo()" will make that you are no longer signed in as that user. So don't use it before you're done. But don't forget to use it!

Oskar Kjellin
+1 This is really the only way to do it.
Nate Bross
Could I keep the "token" variable around a long time, and then use the "using/Undo()" block at various points in tame using the same token?"
JCCyC
I Think so. It's the Impersonate that actually signs in. What I've used is a "GetImpersonation()" that returns a WindowsImpersonationContext like above
Oskar Kjellin
But won't person.Undo() render the person variable unusable for a future access? My plan was to call LogonUser(), keep the "token" pointer, and then use various using blocks exactly like yours, each with creation of a new WindowsImpersonationContext object.
JCCyC
Or maybe I may NOT call Undo() each time, just using(person) { something(); }, then using(person) { something_else(); }, and then when I'm sure I won't need the connection further, person.Undo()?
JCCyC
Then you will dispose your object each time and not being able to undo it (I think). Perhaps instead just leave a person hanging and don't have "using" but instead call dispose explicitly
Oskar Kjellin
You have DuplicateToken in your imports but it is never used in your example, what is that for?
Scott Chamberlain
@Scott It might be for me not cleaning up the code very well :)
Oskar Kjellin