在Windows 2008上以编程方式创建漫游用户配置文件

时间:2010-01-06 18:02:01

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

问题:

通过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  该文件需要有权访问该源的人进行分析,并希望能够解释为什么该配置文件始终是临时加载的。
  2. 环境:

    1. Windows 2008模式域/林
    2. 服务器fs001 - Windows 2008 Standard SP2 x86框
    3. fs001上的文件共享“共享”与Everyone + SYSTEM完全控制共享和ntfs。
    4. 要重现:

      1. 创建域用户tuser1,漫游配置文件设置为\ fs001 \ Share \ tuser1 \ profile
      2. 在fs001上运行此程序作为任何域管理员和本地管理员帐户(如果UAC以管理员身份运行),临时用户配置文件将加载到c:\ users \ temp *而不是c:\ users \ tuser1
      3. ETL可能是最好的方式,并将提供最快的诊断。用户配置文件服务的procmon svchost实例和系统级别的跟踪登录没有透露太多关于出错的地方(如果有必要,我可以提供更多信息,但这是死路一条)。 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();
                        }
                    }
                }
            }
        }
        

        }

1 个答案:

答案 0 :(得分:1)

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

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

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

您可以在HKLM \ Software \ Microsoft \ Windows NT \ CurrentVersion \ ProfileList {sid}中的CentralProfile字符串中自己查看问题。只有当您尝试将LoadUserProfile用于漫游配置文件时,才会显示CentralProfile。