在Windows 2008上以编程方式创build漫游用户configuration文件

问题:

通过LoadUserProfile API为漫游用户login和加载configuration文件不会创build正确的configuration文件。 这只发生在Windows 2008下(UACclosures和开启)。 使用标准的Windowsloginlogin工作正常,相同的代码在Windows 2003上正常工作。

日志:

  1. http://drop.io/6t2b3f3通过命令创build的用户configuration文件服务的ETL:logman -startconfiguration文件-p {eb7428f5-ab1f-4322-a4cc-1f1a9b2c5e98} 255 3 -ets文件需要由有权访问的人分析消息来源,希望能够说明为什么configuration文件始终是临时加载的。

环境:

  1. Windows 2008模式域/林
  2. 服务器fs001 – Windows 2008标准SP2 x86框
  3. fs001上的文件共享与所有人共享系统,共享和NTFS的完全控制。

重现:

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

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

有任何想法吗?

谢谢,Alex

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(); } } } } } 

}

我一直在追查同样的问题几个月,最后得到答案。 我在互联网上的其他地方还没有看到这个答案。 它也会从LoadUserProfile的处理中移除5到10秒。 这些注释仅适用于使用漫游配置文件的情况。

PROFILEINFO中的lpUserName字段至少用于Windows 2008 R2中的活动目录用户验证。 它用于构建漫游配置文件路径和本地配置文件路径。 如果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用于漫游配置文件时才存在。