سؤال

Is there some series of flags that will allow LogonUser to return a token useable for impersonation of a local user when the computer is NOT connected to a network (but all accounts already exist locally).

I have the domain account executing the app

MYDOMAIN\FooUser

and I'm trying to get an impersonation token for

MYLAPTOP\TestUser

Then I read a series of text files in a folder which all can be read by FooUser, but some have the read privilege denied for TestUser.

If I login to Windows and run the app from TestUser the privileges map correctly and permissions are denied on the files. If I'm connected to my domain and run the app from FooUser I can also impersonate TestUser and the file permissions again properly deny access as expected (using LOGON32_LOGON_INTERACTIVE).

The problem occurs when my Ethernet cable is unplugged and I try to call LogonUser for TestUser and I expected that I'd be able to somehow validate local credentials... locally?

Using LOGON32_LOGON_INTERACTIVE:

  • Entering credentials for TestUser returns error indicating "Bad username or password"
  • Entering credentials for FooUser returns error indicating "No logon servers available" (makes sense, I'm not complaining... except how did I sign into Windows in the first place when not connected to my domain?)

Using LOGON32_LOGON_NEW_CREDENTIALS:

  • Entering gibberish credentials returns a token that appears to have the same access as FooUser
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Principal;
using Common.NativeMethods.Enumerations;

namespace Common.NativeMethods
{
    public static class AdvApi32
    {
        // http://www.pinvoke.net/default.aspx/advapi32.logonuser
        // http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.securestringtoglobalallocunicode(v=vs.100).aspx

        // PInvoke into the Win32 API to provide access to the  
        // LogonUser and CloseHandle functions.
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        internal static extern bool LogonUser(
            IntPtr username,
            IntPtr domain,
            IntPtr password,
            LogonType logonType,
            LogonProvider logonProvider,
            ref IntPtr token
        );

        public static WindowsIdentity LogonUser(SecureString p_userName, SecureString p_password, SecureString p_domainName)
        {
            IntPtr UserAccountToken = IntPtr.Zero;

            IntPtr UserNamePointer = IntPtr.Zero;
            IntPtr PasswordPointer = IntPtr.Zero;
            IntPtr DomainNamePointer = IntPtr.Zero;

            try
            {
                // Marshal the SecureString to unmanaged memory.
                UserNamePointer = Marshal.SecureStringToGlobalAllocUnicode(p_password);
                PasswordPointer = Marshal.SecureStringToGlobalAllocUnicode(p_userName);
                DomainNamePointer = Marshal.SecureStringToGlobalAllocUnicode(p_domainName);

                // Call LogonUser, passing the unmanaged (and decrypted) copy of the SecureString password.
                bool ReturnValue =
                    AdvApi32
                        .LogonUser(
                            UserNamePointer,
                            DomainNamePointer,
                            PasswordPointer,
                            LogonType.LOGON32_LOGON_INTERACTIVE, //.LOGON32_LOGON_NEW_CREDENTIALS,
                            LogonProvider.LOGON32_PROVIDER_DEFAULT, //.LOGON32_PROVIDER_WINNT50,
                            ref UserAccountToken);

                // Get the Last win32 Error and throw an exception. 
                if (!ReturnValue && UserAccountToken == IntPtr.Zero)
                {
                    int error = Marshal.GetLastWin32Error();

                    throw
                        new Win32Exception(error);
                }

                // The token that is passed to the following constructor must  
                // be a primary token in order to use it for impersonation.
                return
                    new WindowsIdentity(UserAccountToken);
            }
            finally
            {
                // Zero-out and free the unmanaged string reference.
                Marshal.ZeroFreeGlobalAllocUnicode(UserNamePointer);
                Marshal.ZeroFreeGlobalAllocUnicode(PasswordPointer);
                Marshal.ZeroFreeGlobalAllocUnicode(DomainNamePointer);

                // Close the token handle.
                Kernel32.CloseHandle(UserAccountToken);
            }
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.ConstrainedExecution;
using System.Security;

namespace Common.NativeMethods
{
    // http://msdn.microsoft.com/en-us/library/system.security.principal.windowsimpersonationcontext%28v=vs.100%29.aspx

    public static class Kernel32
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        [SuppressUnmanagedCodeSecurity]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal extern static bool CloseHandle(IntPtr handle);
    }
}
هل كانت مفيدة؟

المحلول

oops... I made a typo when I was refactoring. LogonUser works just fine when not connected to the domain; if you point to the correct parameters at least.

 UserNamePointer = Marshal.SecureStringToGlobalAllocUnicode(p_password);
 PasswordPointer = Marshal.SecureStringToGlobalAllocUnicode(p_userName);

Fixed

 UserNamePointer = Marshal.SecureStringToGlobalAllocUnicode(p_userName);
 PasswordPointer = Marshal.SecureStringToGlobalAllocUnicode(p_password);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top