Программное создание перемещаемых профилей пользователей в Windows 2008

Проблема:

Вход в систему и загрузка профиля через 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 «Общий доступ», при этом все + СИСТЕМА имеют полный контроль над общими ресурсами и NTFS.

Для воспроизведения:

  1. Создайте пользователя домена tuser1 с перемещаемым профилем, установленным на \ fs001 \ Share \ tuser1 \ profile
  2. Запустите эту программу от имени любого администратора домена и учетной записи локального администратора на fs001 (если UAC запущен от имени администратора), временный профиль пользователя загружается в c: \ users \ temp *, а не в c: \ users \ tuser1

ETL, вероятно, лучший способ пойти сюда и предоставит самую быструю диагностику. Проверка экземпляра svchost службы профилей пользователей и отслеживание входа в систему на системном уровне не раскрыли слишком много о том, что происходит не так (при необходимости я могу предоставить дополнительную информацию, но это тупик). Использование userenv.log в Windows 2003 помогло бы, но 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();
                }
            }
        }
    }
}

}


person ao.    schedule 06.01.2010    source источник
comment
Я не вижу, чтобы TOKEN_IMPERSONATE и TOKEN_DUPLICATE запрашивались как привилегии, когда вы получаете токен пользователя ... иногда они есть, но их явный запрос может спровоцировать сбой в этот момент, который укажет вам правильное направление.   -  person Brandon Langley    schedule 28.08.2011
comment
Кроме того, большинство этих функций на основе bool не дадут вам ничего полезного, если они выйдут из строя в данный момент, потому что они не захватывают последнюю ошибку Win32. Прочтите эту статью в блоге, чтобы увидеть быстрый пример того, как это сделать: blogs.msdn.com/b/adam_nathan/archive/2003/04/25/56643.aspx   -  person Brandon Langley    schedule 28.08.2011


Ответы (1)


Я много месяцев гнался за одной и той же проблемой и наконец получил ответ. Я не видел этого ответа больше нигде в Интернете. Это также удаляет от 5 до 10 секунд из обработки LoadUserProfile. Эти комментарии относятся только к ситуациям, когда используются перемещаемые профили.

Поле lpUserName в PROFILEINFO используется не только для проверки пользователей активного каталога, по крайней мере, в Windows 2008 R2. Он используется для построения пути перемещаемого профиля и пути к локальному профилю. Если lpUserName содержит имя домена (mydomain \ myusername), то используемый путь содержит это. Но если вы измените lpUserName, чтобы исключить домен, вызов LoadUserProfile завершится ошибкой.

Однако, если вы используете только имя пользователя в lpUserName и указываете доменное имя NetBIOS в lpServerName, это работает. Имя NetBIOS обычно представляет собой наивысший уровень имени домена (mydomain.parent.com, например, имя mydomain), дополненное или обрезанное до 15 символов плюс шестнадцатеричный 1B (\ u001B в строках C #) в 16-м символе.

Вы можете сами увидеть проблему в строке CentralProfile в HKLM \ Software \ Microsoft \ Windows NT \ CurrentVersion \ ProfileList {sid}. CentralProfile присутствует только при попытке использовать LoadUserProfile для перемещаемых профилей.

person Peter Boswell    schedule 22.12.2011