Среда: Windows XP SP3, C #, .Net 4.0
Проблема:
Я пытаюсь добавить доступ к кусту реестра олицетворенных пользователей в классе олицетворения, и у меня возникают проблемы, связанные с типом олицетворяемого пользователя (или, точнее, кажется, что ограничение олицетворяет пользователя).
Первоначально я следовал примеру олицетворения из CodeProject , в котором был показан вызов LoadUserProfile()
, произошедший после того, как было начато олицетворение с использованием сгенерированного дубликатом токена вызова DuplcateToken()
из исходного токена, полученного из LogonUser()
. Мне не удалось заставить этот пример работать в моей среде, выдавая себя за пользователя с ограниченными правами из учетной записи администратора (из снимков экрана, включенных в пример, кажется, что это было сделано в системе Windows Vista \ 7, и никаких подробностей о типы учетных записей).
При вызове LoadUserProfile()
возникла ошибка «Отказано в доступе». При просмотре userenv.log показывается строка «LoadUserProfile: не удалось включить привилегию восстановления. Ошибка c0000022». Документация LoadUserProfile в MSDN показывает, что вызывающий процесс должен обладать привилегиями SE_RESTORE_NAME и SE_BACKUP_NAME, которые по умолчанию имеют только члены групп «Администраторы» и «Операторы резервного копирования». (В качестве примечания, когда я позже попытался добавить эти две привилегии в группу «Пользователи», я все еще получил «Отказ в доступе», но журнал userenv.log показал, что «DropClientContext: Client [number] не имеет достаточных прав. Ошибка 5» », Я не могу найти информацию о)
Учитывая, что пользователь, под которым я действовал, не имел этих привилегий, я переместил вызов на LoadUserProfile()
до, прежде чем начать олицетворение, и на этот раз он загрузился без проблем, и я смог прочитать и записать его в этом тесте , Думая, что я нашел свой ответ, я создал условную проверку для типа учетной записи, чтобы LoadUserProfile()
вызывался до олицетворения, если текущий пользователь был членом Администраторов, или ждал до окончания после олицетворения, если участник не был членом Администраторов. (в более позднем случае я буду полагаться на пользователя, выдавшего себя за него, имеющего эти привилегии). К сожалению, я был неправ; Я не обнаружил свой ответ. Когда я тестировал вызов с перевернутой ролью (Пользователь> Администратор) Вызов LoadUserProfile()
все еще не удался снова с ошибкой «Отказано в доступе», и файл userenv.log показал то же самое «LoadUserProfile: не удалось включить привилегию восстановления. Ошибка c0000061"но на этот раз с другим номером ошибки.
Считая, что привилегии не могут быть включены по умолчанию для токенов, возвращенных с LogonUser()
и \ или DuplicateToken()
Я добавил два вызова к AdjustTokenPrivilege()
на токене текущих пользователей (происходящем после олицетворения), полученном с WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token
.
TokenAccessLevels.AdjustPrivileges
и TokenAccessLevels.Query
были указаны, поскольку в документации для AdjustTokenPrivilege на MSDN указано, что они необходимы для корректируемого токена (я также пытался получить токен через вызов OpenProcessToken()
с помощью дескриптора, полученного из System.Diagnostics.Process.GetCurrentProcess().Handle
но это не удалось при вызове от пользователя как внутри, так и за пределами олицетворения, при этом GetCurrentProcess()
- это функция, выдавшая отказ в доступе)
AdjustTokenPrivilege()
успешно возвращено при использовании с WindowsIdentity...Token
, но LoadUserProfile()
по-прежнему приводит к отказу в доступе (привилегия восстановления).В этот момент я не был убежден, что AdjustTokenPrivilege()
выполняет свою работу, поэтому я решил определить, какие привилегии были доступны и в каком состоянии они находились для конкретного токена с GetTokenInformation()
, что привело к его собственной небольшой сумке веселья.После изучения некоторых новых вещей я смог вызвать GetTokenInformation()
и распечатать список привилегий и их текущее состояние, но результаты оказались несколько неубедительными, так как Restore и Backup показывали атрибут 0 до и после вызова AdjustTokenPrivilege()
какадминистратор и выдавая себя за администратора (как ни странно, три других привилегии изменились с 2 до 1 на токене при вызове AdjustTokenPrivilege()
, но не те, которые были фактически настроены и остались на значении 0)
Я удалил вызовDuplicateToken()
и заменил все места, которые он использовал, токеном, возвращенным из LogonUser()
, чтобы посмотреть, поможет ли это, хотя при тестировании привилегий на токенах токены LogonUser()
и DuplicateToken()
были идентичны.Когда я изначально писал класс подражания, я без проблем использовал основной токен в своем вызове WindowsImpersonationContext.Impersonate()
и решил, что это стоит попробовать.
В приведенном ниже примере кода я могуолицетворять и получать доступ к реестру пользователя при запуске от имени администратора, но не наоборот.Любая помощь будет принята с благодарностью.
Редактирование перед публикацией:
Я также пытался использовать RegOpenCurrentUser()
API вместо LoadUserProfile()
и добился успеха с администратором> self и Administrator> Олицетворение пользователя, но при олицетворении Администратора из другой учетной записи Администратора или Пользователя RegOpenCurrentUser()
возвращает указатель на HKEY_USERS \ S-1-5-18 (что бы это ни было) вместо куста фактической учетной записи.Я предполагаю, потому что он на самом деле не загружен, что возвращает меня к исходной точке с необходимостью использования LoadUserProfile()
Из документации RegOpenCurrentUser (MSDN):
RegOpenCurrentUser использует потоктокен для доступа к соответствующему ключу или по умолчанию, если профиль не загружен.
Фрагмент кода:
// Private variables used by class
private IntPtr tokenHandle;
private PROFILEINFO pInfo;
private WindowsImpersonationContext thisUser;
private string sDomain = string.Empty;
private string sUsername = string.Empty;
private string sPassword = string.Empty;
private bool bDisposed = false;
private RegistryKey rCurrentUser = null;
private SafeRegistryHandle safeHandle = null;
//Constants used for privilege adjustment
private const string SE_RESTORE_NAME = "SeRestorePrivilege";
private const string SE_BACKUP_NAME = "SeBackupPrivilege";
private const UInt32 SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001;
private const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
private const UInt32 SE_PRIVILEGE_REMOVED = 0x00000004;
private const UInt32 SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000;
[StructLayout(LayoutKind.Sequential)]
protected struct PROFILEINFO {
public int dwSize;
public int dwFlags;
[MarshalAs(UnmanagedType.LPTStr)]
public String lpUserName;
[MarshalAs(UnmanagedType.LPTStr)]
public String lpProfilePath;
[MarshalAs(UnmanagedType.LPTStr)]
public String lpDefaultPath;
[MarshalAs(UnmanagedType.LPTStr)]
public String lpServerName;
[MarshalAs(UnmanagedType.LPTStr)]
public String lpPolicyPath;
public IntPtr hProfile;
}
protected struct TOKEN_PRIVILEGES {
public UInt32 PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public LUID_AND_ATTRIBUTES[] Privileges;
}
[StructLayout(LayoutKind.Sequential)]
protected struct LUID_AND_ATTRIBUTES {
public LUID Luid;
public UInt32 Attributes;
}
[StructLayout(LayoutKind.Sequential)]
protected struct LUID {
public uint LowPart;
public int HighPart;
}
// Private API calls used by class
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
protected static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
protected static extern bool LoadUserProfile(IntPtr hToken, ref PROFILEINFO lpProfileInfo);
[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
protected static extern bool UnloadUserProfile(IntPtr hToken, IntPtr hProfile);
[DllImport("kernel32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
protected static extern bool CloseHandle(IntPtr hObject);
[DllImport("advapi32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
protected static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, UInt32 Zero, IntPtr Null1, IntPtr Null2);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)][return: MarshalAs(UnmanagedType.Bool)]
protected static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid);
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Start() {
tokenHandle = IntPtr.Zero; // set the pointer to nothing
if (!LogonUser(sUsername, sDomain, sPassword, 2, 0, ref tokenHandle)) {
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
} // end if !LogonUser returned false
try { //All of this is for loading the registry and is not required for impersonation to start
LUID LuidRestore = new LUID();
LUID LuidBackup = new LUID();
if(LookupPrivilegeValue(null, SE_RESTORE_NAME, ref LuidRestore) && LookupPrivilegeValue(null, SE_BACKUP_NAME, ref LuidBackup)) {
//Create the TokenPrivileges array to pass to AdjustTokenPrivileges
LUID_AND_ATTRIBUTES[] LuidAndAttributes = new LUID_AND_ATTRIBUTES[2];
LuidAndAttributes[0].Luid = LuidRestore;
LuidAndAttributes[0].Attributes = SE_PRIVILEGE_ENABLED;
LuidAndAttributes[1].Luid = LuidBackup;
LuidAndAttributes[1].Attributes = SE_PRIVILEGE_ENABLED;
TOKEN_PRIVILEGES TokenPrivileges = new TOKEN_PRIVILEGES();
TokenPrivileges.PrivilegeCount = 2;
TokenPrivileges.Privileges = LuidAndAttributes;
IntPtr procHandle = WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token;
if(AdjustTokenPrivileges(procHandle, false, ref TokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero)) {
pInfo = new PROFILEINFO();
pInfo.dwSize = Marshal.SizeOf(pInfo);
pInfo.lpUserName = sUsername;
pInfo.dwFlags = 1;
LoadUserProfile(tokenHandle, ref pInfo); //this is not required to take place
if(pInfo.hProfile != IntPtr.Zero) {
safeHandle = new SafeRegistryHandle(pInfo.hProfile, true);
rCurrentUser = RegistryKey.FromHandle(safeHandle);
}//end if pInfo.hProfile
}//end if AdjustTokenPrivileges
}//end if LookupPrivilegeValue 1 & 2
}catch{
//We don't really care that this didn't work but we don't want to throw any errors at this point as it would stop impersonation
}//end try
WindowsIdentity thisId = new WindowsIdentity(tokenHandle);
thisUser = thisId.Impersonate();
} // end function Start