【问题标题】:Win32 API LogonUser Offline Access to Local AccountWin32 API LogonUser 离线访问本地帐户
【发布时间】:2014-07-01 20:39:21
【问题描述】:

当计算机未连接到网络(但所有帐户已在本地存在)时,是否有一些标志允许LogonUser 返回可用于模拟本地用户的令牌。

我有执行应用程序的域帐户

MYDOMAIN\FooUser

我正在尝试获取模拟令牌

MYLAPTOP\TestUser

然后我读取了一个文件夹中的一系列文本文件,FooUser 都可以读取这些文件,但有些文件对TestUser 的读取权限被拒绝。

如果我登录到 Windows 并从 TestUser 运行应用程序,则权限映射正确并且文件权限被拒绝。如果我连接到我的域并从FooUser 运行应用程序,我还可以模拟TestUser 并且文件权限再次按预期正确拒绝访问(使用LOGON32_LOGON_INTERACTIVE)。

当我的以太网电缆被拔掉并且我尝试为TestUser 调用LogonUser 并且我希望我能够以某种方式在本地验证本地凭据时会出现问题...本地?

使用LOGON32_LOGON_INTERACTIVE

  • 输入TestUser 的凭据返回错误,指出“用户名或密码错误”
  • 输入FooUser 的凭据返回错误,指示“没有可用的登录服务器”(有道理,我没有抱怨...除了在未连接到我的域时我是如何登录到 Windows 的?)

使用LOGON32_LOGON_NEW_CREDENTIALS

  • 输入乱码凭据会返回一个似乎与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);
    }
}

【问题讨论】:

  • 您是不是想说 LogonUser 在尝试获取本地“TestUser”帐户的用户令牌时失败,这是您的问题?
  • 不完全。它在我连接到公司域网络时有效,但在我离线时无效。请注意,这是一个 WinForms 应用程序。我想知道是否有某种方法可以访问缓存的凭据……因为我显然可以在笔记本电脑上登录FooUserTestUser,而无需连接到我的公司网络。在未连接时,我无法找到Impersonate 任一用户(但更重要的是TestUser)的方法。
  • 冒充TestUser时,如何获取TestUser的token?更具体地说,调用 LogonUser 时的域和用户参数是什么?你有没有尝试过 ”。”作为 TestUser 的(本地)域名?
  • 是的,当我的笔记本电脑连接到我的公司网络时,当然可以。也就是说,我在工作时插入了以太网电缆。但是,如果我尝试在家中处理这些东西,我将无法再使用LogonUserImpersonate() 任何一个用户,因为没有域服务器来处理我的身份验证请求。但我仍然可以从 Windows 登录屏幕登录任一帐户并进入 Windows 桌面,因此很明显凭据缓存在某处...

标签: c# winapi


【解决方案1】:

哎呀...我在重构时打错了字。 LogonUser 在未连接到域时工作得很好;如果您至少指向正确的参数。

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

固定

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    • 2015-05-08
    • 2022-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多