【问题标题】:Creating roaming user profiles programmatically on Windows 2008在 Windows 2008 上以编程方式创建漫游用户配置文件
【发布时间】:2011-01-02 04:10:33
【问题描述】:

问题:

通过 LoadUserProfile API 为漫游用户登录和加载配置文件未创建正确的配置文件。这只发生在 Windows 2008 下(UAC 关闭和打开)。使用标准 Windows 登录方式登录可以正常工作,并且相同的代码在 Windows 2003 上也可以正常工作。

日志:

  1. http://drop.io/6t2b3f3 通过命令创建的用户配置文件服务的 ETL: logman -start profile -p {eb7428f5-ab1f-4322-a4cc-1f1a9b2c5e98} 255 3 –ets 该文件需要由有权访问源代码的人进行分析,希望这能阐明为什么该配置文件总是作为临时文件加载。

环境:

  1. Windows 2008 模式域/林
  2. 服务器 fs001 - Windows 2008 Standard SP2 x86 盒
  3. 在 fs001“共享”上共享文件,Everyone+SYSTEM 可以完全控制共享和 ntfs。

重现:

  1. 创建域用户 tuser1 并将漫游配置文件设置为 \fs001\Share\tuser1\profile
  2. 在 fs001 上以任何域管理员和本地管理员帐户运行此程序(如果 UAC 以管理员身份运行),临时用户配置文件将加载到 c:\users\temp* 而不是 c:\users\tuser1李>

ETL 可能是最好的方法,它将提供最快的诊断。用户配置文件服务 svchost 实例的 Procmon 和系统级别的跟踪登录并没有透露太多关于出了什么问题(如果需要,我可以提供更多信息,但这是一个死胡同)。 Windows 2003 上的 userenv.log 会有所帮助,但 ETL 只能由 MSFT 的人员进行分析。

有什么想法吗?

谢谢, 亚历克斯

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace consoleLogon {
  class Program {
    #region Helpers for setting privilegies functionality
    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    public struct LUID_AND_ATTRIBUTES {
        public long Luid;
        public int Attributes;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    public struct TOKEN_PRIVILEGES {
        public int PrivilegeCount;
        public LUID_AND_ATTRIBUTES Privileges;
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool AdjustTokenPrivileges(
        IntPtr TokenHandle,
        bool DisableAllPrivileges,
        ref TOKEN_PRIVILEGES NewState,
        int BufferLength,
        IntPtr PreviousState,
        IntPtr ReturnLength
        );

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool LookupPrivilegeValue(
        string lpSystemName,
        string lpName,
        ref long lpLuid
        );

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool OpenProcessToken(
        IntPtr ProcessHandle,
        int DesiredAccess,
        ref IntPtr TokenHandle
        );

    public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
    public const int TOKEN_QUERY = 0x00000008;
    public const int TOKEN_DUPLICATE = 0x00000002;
    public const int TOKEN_IMPERSONATE = 0x00000004;
    public const int SE_PRIVILEGE_ENABLED = 0x00000002;
    public const string SE_RESTORE_NAME = "SeRestorePrivilege";
    public const string SE_BACKUP_NAME = "SeBackupPrivilege";

    [DllImport("advapi32.dll", SetLastError = true)]
    static public extern bool LogonUser(String lpszUsername, String lpszDomain,
        String lpszPassword,
        int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("Userenv.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool LoadUserProfile(
        IntPtr hToken,               // user token
        ref PROFILEINFO lpProfileInfo  // profile
        );

    [DllImport("Userenv.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool UnloadUserProfile(
        IntPtr hToken,   // user token
        IntPtr hProfile  // handle to registry key
        );

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private extern static bool DuplicateToken(
        IntPtr ExistingTokenHandle,
        int SECURITY_IMPERSONATION_LEVEL,
        ref IntPtr DuplicateTokenHandle
        );


    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct PROFILEINFO {
        public static readonly int SizeOf = Marshal.SizeOf(typeof(PROFILEINFO));

        public int dwSize;                 // Set to sizeof(PROFILEINFO) before calling
        public int dwFlags;                // See PI_ flags defined in userenv.h
        public string lpUserName;          // User name (required)
        public string lpProfilePath;       // Roaming profile path (optional, can be NULL)
        public string lpDefaultPath;       // Default user profile path (optional, can be NULL)
        public string lpServerName;        // Validating domain controller name in netbios format (optional, can be NULL but group NT4 style policy won't be applied)
        public string lpPolicyPath;        // Path to the NT4 style policy file (optional, can be NULL)
        public IntPtr hProfile;            // Filled in by the function.  Registry key handle open to the root.
    }
    #endregion

    static void Main(string[] args) {
        string domain = "dev1";
        string userName = "tuser1";
        string password = "asd!234";
        string profilePath = @"\\fs001\TestProfiles\tuser9\profile";

        bool retVal = false;
        IntPtr primaryToken = IntPtr.Zero;
        IntPtr dupeToken = IntPtr.Zero;
        PROFILEINFO profileInfo = new PROFILEINFO();
        try {
            // Add RESTORE AND BACUP privileges to process primary token, this is needed for LoadUserProfile function
            IntPtr processToken = IntPtr.Zero;

            OpenProcessToken(System.Diagnostics.Process.GetCurrentProcess().Handle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref processToken);

            long luidRestore = 0;
            long luidBackup = 0;
            retVal = LookupPrivilegeValue(null, SE_RESTORE_NAME, ref luidRestore);
            retVal = LookupPrivilegeValue(null, SE_BACKUP_NAME, ref luidBackup);

            TOKEN_PRIVILEGES tpRestore = new TOKEN_PRIVILEGES();
            TOKEN_PRIVILEGES tpBackup = new TOKEN_PRIVILEGES();

            tpRestore.PrivilegeCount = 1;
            tpRestore.Privileges = new LUID_AND_ATTRIBUTES();
            tpRestore.Privileges.Attributes = SE_PRIVILEGE_ENABLED;
            tpRestore.Privileges.Luid = luidRestore;

            tpBackup.PrivilegeCount = 1;
            tpBackup.Privileges = new LUID_AND_ATTRIBUTES();
            tpBackup.Privileges.Attributes = SE_PRIVILEGE_ENABLED;
            tpBackup.Privileges.Luid = luidBackup;

            retVal = AdjustTokenPrivileges(processToken, false, ref tpRestore, 0, IntPtr.Zero, IntPtr.Zero);
            if (false == retVal) {
                throw new Win32Exception();
            }
            retVal = AdjustTokenPrivileges(processToken, false, ref tpBackup, 0, IntPtr.Zero, IntPtr.Zero);
            if (false == retVal) {
                throw new Win32Exception();
            }

            // Logon as user + password in clear text for sake of simple sample (protocol transitioning is better).
            retVal = LogonUser(userName, domain, password, 3 /* LOGON32_LOGON_NETWORK */, 0 /*LOGON32_PROVIDER_DEFAULT */, ref primaryToken);
            if (false == retVal) {
                throw new Win32Exception();
            }

            // Duplicate primary token.
            // LoadUserProfile needs a token with TOKEN_IMPERSONATE and TOKEN_DUPLICATE access flags.
            retVal = DuplicateToken(primaryToken, 2 /* securityimpersonation */, ref dupeToken);
            if (false == retVal) {
                throw new Win32Exception();
            }

            // Load user profile for roaming profile
            profileInfo.dwSize = PROFILEINFO.SizeOf;
            profileInfo.lpUserName = domain + @"\" + userName;
            profileInfo.lpProfilePath = profilePath;

            Console.WriteLine("UserName: {0}", profileInfo.lpUserName);
            Console.WriteLine("ProfilePath: {0}", profileInfo.lpProfilePath);

            retVal = LoadUserProfile(dupeToken, ref profileInfo);
            if (false == retVal) {
                throw new Win32Exception();
            }

            // What should happen
            // 1.  Local new profile in c:\users\tuser1.dev1 folder with copy from default.
            // 2.  Valid user registry hive ntuser.dat
            // 3.  Loaded profile session entry in the registry entry
            //     HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\SID of tuser1\
            //     State bit mask should have new local profile (x004), new central profile (x008), 
            //     update the central profile (0010).
            //
            //       "State"=dword:0000021c
            //       "CentralProfile"="\\fs001\Share\tuser1\profile.V2"
            //       "ProfileImagePath"="C:\Users\tuser1"
            //
            //     See http://technet.microsoft.com/en-us/library/cc775560(WS.10).aspx fpr more info
            //     Roaming Profile - New User section.
            // 
            // What actually happens:
            // 1.  Temp profile is loaded in c:\users\temp
            // 2.  Registry entry ProfieList/SID is showing temporary profile
            //       "State"=dword:00000a04
            //       "CentralProfile"="\\fs001\Share\tuser1\profile.V2"
            //       "ProfileImagePath"="C:\Users\TEMP"

            Console.WriteLine("Profile loaded, hit enter to unload profile");
            Console.ReadLine();
        }
        catch (Exception e) {
            Console.WriteLine(e.ToString());
            Console.ReadLine();
        }
        finally {
            // Unload profile properly.
            if (IntPtr.Zero != dupeToken) {
                retVal = UnloadUserProfile(dupeToken, profileInfo.hProfile);
                if (false == retVal) {
                    throw new Win32Exception();
                }
            }
        }
    }
}

}

【问题讨论】:

  • 当您获得用户的令牌时,我没有看到 TOKEN_IMPERSONATE 和 TOKEN_DUPLICATE 被请求为特权...有时它们在那里,但明确请求它们可能会在那时引发失败,这将指向您正确的方向。
  • 此外,大多数基于 bool 的函数如果此时失败,将不会给您任何有用的信息,因为它们没有捕获 Win32 的最后一个错误。查看此博客文章以获取有关如何执行此操作的快速示例:blogs.msdn.com/b/adam_nathan/archive/2003/04/25/56643.aspx

标签: c# windows windows-server-2008 profile advapi32


【解决方案1】:

我几个月来一直在寻找同样的问题,终于有了答案。我在互联网上的其他任何地方都没有看到这个答案。它也从 LoadUserProfile 的处理中删除了 5 到 10 秒。这些 cmets 仅适用于使用漫游配置文件的情况。

至少在 Windows 2008 R2 中,PROFILEINFO 中的 lpUserName 字段不仅仅用于活动目录用户验证。它用于构建漫游配置文件路径和本地配置文件路径。如果 lpUserName 包含域名 (mydomain\myusername),则使用的路径包含此名称。但是,如果您更改 lpUserName 以排除域,则 LoadUserProfile 调用将失败。

但是,如果您只使用 lpUserName 中的用户名并在 lpServerName 中提供 NetBIOS 域名,它就可以工作。 NetBIOS 名称通常是域名的最高级别(mydomain.parent.com,例如名称为 mydomain),在第 16 个字符中填充或修剪为 15 个字符加上十六进制 1B(C# 字符串中的 \u001B)。

您可以在 HKLM\Software\Microsoft\Windows NT\CurrentVersion\ProfileList{sid} 中的 CentralProfile 字符串中自己查看问题。 CentralProfile 仅在您尝试将 LoadUserProfile 用于漫游配置文件时出现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-07
    相关资源
    最近更新 更多