2010-12-08 7 views
11

Umgebung: Windows XP SP3, C#, .NET 4.0Identitätswechsel und Currentregistrierungszugriff

Problem:

Ich versuche, den Zugang zu einer imitierten Benutzer Registrierungsstruktur in einer Identitätswechsel Klasse hinzuzufügen, und ich bin Es treten Probleme auf, die auf dem Typ des Benutzers basieren, der imitiert wird (oder genauer gesagt scheint die Einschränkung auf dem sich imitierenden Benutzer zu liegen).

ich ursprünglich folgende an impersonation example from CodeProject, die einen Aufruf an LoadUserProfile() stattfindet zeigte nach Identitätswechsel gestartet wurde ein Duplikat Token througha Anruf von der ursprünglichen Token von LogonUser() erhalten DuplcateToken() erzeugt. Ich konnte dieses Beispiel in meiner Umgebung nicht funktionieren lassen, indem ich mich als ein Benutzer mit einem eingeschränkten Benutzer von einem Administratorkonto ausgab (aus den in dem Beispiel enthaltenen Screenshots sieht es so aus, als ob es auf einem Windows Vista \ 7 System ausgeführt worden wäre Kontotypen).

Der Anruf an LoadUserProfile() warf einen Fehler von "Zugriff verweigert". Ein Blick auf userenv.log zeigte die Zeile "LoadUserProfile: Fehler beim Aktivieren der Wiederherstellungsberechtigung. Error c0000022". Die LoadUserProfile-Dokumentation auf MSDN zeigt, dass der aufrufende Prozess die Berechtigungen SE_RESTORE_NAME und SE_BACKUP_NAME besitzen muss, die standardmäßig nur Mitglieder der Gruppen Administratoren und Sicherungsoperatoren haben. (Als eine Randnotiz, als ich später versuchte, diese beiden Privilegien der Benutzergruppe hinzuzufügen, erhielt ich noch Zugriff verweigert, aber die Datei userenv.log zeigte "DropClientContext: Client [Nummer] hat nicht genug Erlaubnis. Fehler 5" was ich konnte ‚t keine Informationen finden auf)

Da der Benutzer ich tat, war imitiert diese Privilegien nicht habe ich den Anruf zu LoadUserProfile() bewegt, den er vor dem Identitätswechsel starten und diesmal ist es ohne Probleme geladen, und ich war in der Lage zu lesen und schreibe dazu in diesem Test. Mit dem Gedanken, dass ich meine Antwort gefunden habe, habe ich eine bedingte Prüfung für den Kontotyp erstellt, so dass LoadUserProfile() vor dem Identitätswechsel aufgerufen würde, wenn der aktuelle Benutzer ein Mitglied von Administratoren war oder bis zum Identitätswechsel warten würde, wenn das Mitglied kein Mitglied von Administratoren war die spätere Instanz würde ich auf den imitierte Benutzer mit diesen Privilegien verlassen). Leider habe ich mich geirrt; Ich hatte meine Antwort nicht gefunden. Wenn ich den Anruf mit der umgekehrten Rolle (Benutzer> Administrator) getestet habe, ist der Aufruf von LoadUserProfile() immer noch mit dem Fehler Zugriff verweigert fehlgeschlagen, und die Datei userenv.log zeigte dasselbe "LoadUserProfile: konnte die Wiederherstellungsberechtigung nicht aktivieren. Fehler c0000061" aber mit diesmal eine andere Fehlernummer.

Denken, dass die Privilegien nicht standardmäßig auf den Token aktiviert sein, zurückgegeben von LogonUser() und \ oder DuplicateToken() Ich habe zwei Anrufe AdjustTokenPrivilege() auf der aktuellen Benutzer-Token (Ort nach Identitätswechsel Einnahme) von WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token erhalten. TokenAccessLevels.AdjustPrivileges und TokenAccessLevels.Query angegeben wurden, weil die Dokumentation für AdjustTokenPrivilege auf MSDN gibt an, dass sie auf dem Token benötigt werden eingestellt wird (ich habe auch versucht, das Token durch einen Aufruf OpenProcessToken() Erhalten eines Griffs von System.Diagnostics.Process.GetCurrentProcess().Handle abgerufen wird, aber das scheiterte, wenn sie von dem Nutzer aufgerufen sowohl innerhalb als auch außerhalb des Identitätswechsels mit GetCurrentProcess() die Funktion ist, die den Zugriff warf verweigert)

AdjustTokenPrivilege() erfolgreich zurückgegeben werden, wenn mit dem WindowsIdentity...Token verwendet, aber LoadUserProfile() noch in Access Denied (Restore Privileg geführt). An diesem Punkt war ich nicht davon überzeugt, dass AdjustTokenPrivilege() seinen Job machte, also begann ich festzustellen, welche Privilegien verfügbar waren und in welchem ​​Zustand sie für ein bestimmtes Token mit GetTokenInformation() waren, was in seiner eigenen kleinen Tüte Spaß resultierte. Nachdem ich einige neue Dinge gelernt hatte, konnte ich GetTokenInformation() anrufen und eine Liste von Privilegien und ihren aktuellen Status ausdrucken, aber die Ergebnisse waren etwas nicht aussagekräftig, da sowohl Wiederherstellen als auch Backup das Attribut 0 vor und nach dem Aufruf von AdjustTokenPrivilege() sowohl als Administrator als auch während der Identitätsübergabe zeigten der Administrator (Seltsamerweise drei andere Privilegien änderte sich von 2 auf 1 auf dem Token beim Aufruf AdjustTokenPrivilege(), aber nicht die tatsächlich eingestellt werden, die bei einem Wert von 0 blieb)

Ich entfernte den Anruf zu DuplicateToken() und ersetzte alle Orte es wurde mit dem Token verwendet, das von LogonUser() zurückgegeben wurde, um zu sehen, ob das beim Testen der Privilegien auf den Token helfen würde, die Tokens LogonUser() und DuplicateToken() waren identisch. Als ich anfänglich die Identitätswechsel-Klasse geschrieben habe, habe ich das primäre Token in meinem Anruf an WindowsImpersonationContext.Impersonate() ohne irgendwelche Probleme verwendet und dachte, dass es einen Versuch wert war.

In dem Codebeispiel, das ich unten angegeben habe, bin ich in der Lage, die Registrierung eines Benutzers zu imitieren und auf sie zuzugreifen, wenn ich als Administrator ausgeführt werde, aber nicht umgekehrt. Jede Hilfe würde sehr geschätzt werden.

Pre-Post Edit:

Ich habe auch den RegOpenCurrentUser() API anstelle von LoadUserProfile() versucht, mit und hatte Erfolg mit Administrator> Selbst und Administrator> Benutzeridentitätswechseln aber wenn Identitätswechsel ein Administrator entweder von einem anderen Administrator-Konto oder einem Benutzer RegOpenCurrentUser() gibt einen Zeiger auf HKEY_USERS \ S-1-5-18 (was immer das ist) statt der tatsächlichen Kontobewegung zurück. Ich vermute, weil es nicht tatsächlich geladen wird, die mir eine wieder auf Platz bringt mit LoadUserProfile()

Von RegOpenCurrentUser Dokumentation (MSDN) zu verwenden, benötigen:

RegOpenCurrentUser verwendet das Thread-Token den entsprechenden Schlüssel zuzugreifen, oder der Standardwert, wenn das Profil nicht geladen ist.

Code Snippet:

// 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 
+0

Ich weiß nicht, was ich gestern getan hatte, dass es funktioniert, aber heute RegOpenCurrentUser gibt den Standard egal was es Konto aus (außer Selbstidentitätswechsel, da der Bienenstock bereits geladen ist) laufen so, soweit ich sagen kann, RegOpenCurrentUser ist wertlos, wenn das Benutzerprofil nicht geladen ist. – JoshHetland

+0

Gibt es eine Möglichkeit, dies zu stoßen, damit die Leute es sehen oder muss ich es umbuchen? – JoshHetland

+2

Aus den Loaduserprofilen docs: „Beginnend mit Windows XP Service Pack 2 (SP2) und Windows Server 2003 muss der Anrufer einen Administrator oder das Konto Localsystem ist Es ist nicht ausreichend, wenn der Anrufer den Administrator oder Konto Local lediglich zu imitieren..“ Wenn Ihr Prozess als normaler Benutzer gestartet wird, haben Sie kein Glück. Sie könnten möglicherweise einen neuen Prozess (unter den Admin-Anmeldeinformationen) starten, um das Profil zu laden. – arx

Antwort

7

Vom LoadUserProfile docs:

Beginnend mit Windows XP Service Pack 2 (SP2) und Windows Server 2003, der Anrufer muss ein Administrator oder die Local sein Konto. Es reicht nicht aus, wenn der Anrufer lediglich die Identität des Administrators oder des lokalen Systemkontos übernimmt.

Wenn Ihr Prozess als normaler Benutzer gestartet wird, haben Sie kein Glück. Sie könnten möglicherweise einen neuen Prozess (unter den Admin-Anmeldeinformationen) starten, um das Profil zu laden.

0

Ich habe festgestellt, dass der Anmeldetyp, der im Aufruf von LogonUser() gesetzt wird, ein Faktor sein kann. Selbst wenn ich als Administrator arbeiten würde, konnte ich den Fehler nicht überwinden, wenn ich nicht von LOGON32_LOGON_INTERACTIVE zu LOGON32_LOGON_BATCH wechselte. Sie müssen sicherstellen, dass die Gruppenrichtlinie "Als Batch-Job anmelden" jedoch nicht beeinträchtigt wird.

Verwandte Themen