Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Per ProcessId prüfen, ob der Benutzer Admin ist (oder: ProcessId -> UserTokenHandle) (https://www.delphipraxis.net/181445-per-processid-pruefen-ob-der-benutzer-admin-ist-oder-processid-usertokenhandle.html)

CodeX 15. Aug 2014 01:29

Per ProcessId prüfen, ob der Benutzer Admin ist (oder: ProcessId -> UserTokenHandle)
 
Ich habe heute sehr viel Zeit mit diesem Problem verbracht und weiß irgendwie nicht mehr weiter. Vielleicht ist es auch nur eine Kleinigkeit oder ein blöder Denkfehler.

Grundsätzliche Problemstellung: Aus einem Service heraus anhand der bereits vorhandenen ProcessId eines laufenden Programms herausfinden, ob der zugehörige Benutzer Mitglied der Administratorgruppe ist.

Im Programm wird die ProcessId per
Delphi-Quellcode:
GetCurrentProcessId
bestimmt und an den Service übermittelt (darauf habe ich nur bedingt Einfluss). Im Service muss ich nun mit dieser ProcessId arbeiten. Meine Herangehensweise ist nun, dass ich darüber das zugehörige UserToken bzw. das UserTokenHandle bestimme und dieses per MSDN-Library durchsuchenCheckTokenMembership prüfe.
Delphi-Quellcode:
// Variante 1 mit JWSCL (Jedi Security Library) (verkürzt):
  var UserToken: TJwSecurityToken;
  UserToken := TJwSecurityToken.CreateTokenByProcessId(ProcessId, MAXIMUM_ALLOWED);
  bAdmin := UserInGroup(DOMAIN_ALIAS_RID_ADMINS, UserToken.TokenHandle);

// Variante 2:
  var hProcessHandle, hUserToken: THandle;
  hProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessId);
  if hProcessHandle <> 0 then
  try
    if OpenProcessToken(hProcessHandle, TOKEN_ALL_ACCESS, hUserToken) then
      bAdmin := UserInGroup(DOMAIN_ALIAS_RID_ADMINS, hUserToken);
  finally
    CloseHandle(hProcessHandle);
  end;
Die UserInGroup-Funktion stammt in dem Fall von hier, ich teste es aber parallel auch mit dieser etwas umfangreicheren TokenTools-Unit mit gleichem Ergebnis.

Der Benutzer wird einfach nicht als Admin erkannt. Vermutlich hat es mit dem Service nichts zu tun, denn ich habe die komplette Abfrage mal in eine normale Anwendung gepackt, mit dem gleichen Ergebnis, aber der Vollständigkeit halber sei die Service-Nutzung erwähnt. Interessant ist aber, dass wenn ich in der Anwendung 0 als UserTokenHandle (= User des aktuellen Prozesses) verwende, der Test vollständig wie gewünscht funktioniert (Erkennen von Admin und Nicht-Admin)! Ich deute es daher so, dass die UserInGroup-Funktion bzw. die TokenTools-Unit richtig funktionieren, aber irgendetwas mit dem erzeugten UserTokenHandle nicht stimmt (und zwar in beiden Varianten), oder auch schon GetCurrentProcessId der falsche Ansatz ist.

Ich hoffe, es weiß jemand Rat...

himitsu 15. Aug 2014 08:03

AW: Per ProcessId prüfen, ob der Benutzer Admin ist (oder: ProcessId -> UserTokenHand
 
Was passiert, wenn du mal auf die wahnwitzige Idee kommst und die Rückgabewerte mit prüfen tätest? :stupid:

Und warum wurde hUserToken nicht wieder geschlossen?

z.B.:
Delphi-Quellcode:
  hProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessId);
  if hProcessHandle = 0 then
    RaiseLasrOSError; // man mag es nicht glauben, aber viele APIs sagen, warum sie nicht funktionierten
  try
    if not OpenProcessToken(hProcessHandle, TOKEN_ALL_ACCESS, hUserToken) then
      RaiseLasrOSError;
    try
      bAdmin := UserInGroup(DOMAIN_ALIAS_RID_ADMINS, hUserToken);
    finally
      CloseHandle(hUserToken); // das fehlte auch noch
    end;
  finally
    CloseHandle(hProcessHandle);
  end



function UserInGroup(Group: DWORD; hUserToken: ...): Boolean;
var
  pIdentifierAuthority: TSIDIdentifierAuthority;
  pSid : Windows.PSID;
  IsMember: BOOL;
begin
  pIdentifierAuthority := SECURITY_NT_AUTHORITY;
  Result := AllocateAndInitializeSid(pIdentifierAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, Group, 0, 0, 0, 0, 0, 0, pSid);
  if Result then
    try
      if not CheckTokenMembership(hUserToken, pSid, IsMember) then
        RaiseLastOSError;
      Result := IsMember;
    finally
      FreeSid(pSid);
    end
  else
    RaiseLastOSError;
end;

CodeX 15. Aug 2014 09:48

AW: Per ProcessId prüfen, ob der Benutzer Admin ist (oder: ProcessId -> UserTokenHand
 
Kurz vorab: Ich habe den geposteten Code stark gekützt. Im Original-Code habe ich mehrere Debug-Log-Ausgaben und auch alle Handles werden geschlossen (das hätte ich natürlich auch mitkopieren sollen).

Was ich allerdings tatsächlich noch nicht gemacht hatte, die bereits gegebenen Funktionen mit RaiseLastOSError-Ausgaben auszustatten. Danke für den Hinweis!

CheckTokenMembership wirft:
Code:
'System Error. Code: 1309.
Ein Thread, der zurzeit nicht die Identität eines Clients angenommen hat, hat versucht, ein Identitätstoken einem Vorgang zu unterziehen'.
:gruebel:

CodeX 15. Aug 2014 10:48

AW: Per ProcessId prüfen, ob der Benutzer Admin ist (oder: ProcessId -> UserTokenHand
 
Ich habe ein wenig recherchiert und bin nur etwas weiter. Zumindest bei der Jwscl-Variante muss das UserToken wohl erst konvertiert werden.
Ich vermute, das müsste so gehen:
Delphi-Quellcode:
  var UserToken: TJwSecurityToken;
  UserToken := TJwSecurityToken.CreateTokenByProcessId(ProcessId, MAXIMUM_ALLOWED);
  UserToken.ConvertToImpersonatedToken(SecurityImpersonation, MAXIMUM_ALLOWED); //<-NEU
  bAdmin := UserInGroup(DOMAIN_ALIAS_RID_ADMINS, UserToken.TokenHandle);
Zumindest wirft das keine Exception mehr. CheckTokenMembership schließt aber anschließend mit IsMember=False ab (was nicht stimmt).
Also entweder meine Konvertierung ist falsch oder es liegt noch wo anders dran.
In der zweiten Variante weiß ich nicht, wie ich das UserTokenHandle konvertieren könnte...

Edit1: Das beschriebene Ergebnis ist aus dem Service heraus (so wie ich es letztlich brauche)! Beim Test aus der Desktopanwendung scheint es zu funktionieren. Was muss man denn im Service anders machen?

Edit2: Für die zweite Variante wäre das wohl:
Delphi-Quellcode:
  if not OpenProcessToken(hProcessHandle, TOKEN_ALL_ACCESS, hUserToken) then
    RaiseLastOSError;
  try
    if not DuplicateToken(hUserToken, SecurityIdentification, @hUserTokenDuplicate) then //<-NEU
      RaiseLastOSError;
    try
      bAdmin := UserInGroup(DOMAIN_ALIAS_RID_ADMINS, hUserTokenDuplicate);
Aber das hat das gleiche Resultat wie der zuvor beschriebene Jwscl-Versuch: Das Ergebnis ist IsMember=False, was nicht stimmt, da das ProcessHandle von einer Anwendung stammt, die in einem Administrator-Account gestartet wurde.

Was stimmt denn da nicht? :'(

CodeX 27. Aug 2014 23:32

AW: Per ProcessId prüfen, ob der Benutzer Admin ist (oder: ProcessId -> UserTokenHand
 
Falls es jemanden interessieren sollte: Ich bin dem Problem auf die Spur gekommen. Die Ursache für das "falsche" Ergebnis der Abfrage liegt darin, dass anscheinend das UserToken bei einem durch UAC kontrollierten Benutzerkonto nicht wirklich den Gruppen-Benutzer representiert, den man glaubt zu prüfen. Das UserToken ist ein eingeschränktes Token (klar) und ist daher wohl automatisch lediglich Mitglied der normalen Benutzergruppe (nicht so klar). Das echte Token des eingeloggten Benutzers ist das LinkedToken davon. Dieses hat nicht nur die echten Administrator-Rechte sondern nur dieses lässt sich auf die korrekte Gruppenzugehörigkeit prüfen.
Ich wundere mich nur, dass ich dutzende Beispiele zur Prüfung der Gruppenzugehörigkeit gefunden habe, aber in keinem auch nur ein Wort zu dieser Problematik verloren wurde. Arbeiten alle mit Windows XP? Oder ohne UAC? Oder merkt einfach keiner, dass der Test ein falsches Ergebnis liefert?

Naja, hier ein bisschen Code dazu (nur der relevante Teil zur Veranschaulichung!), falls irgendwann mal jemand über das gleiche Problem stoßen sollte. In dem Fall habe ich es mit JWSCL gelöst.
Delphi-Quellcode:
...
  DuplicateTokenEx(UserToken.LinkedToken.TokenHandle, MAXIMUM_ALLOWED, nil, SecurityImpersonation, TokenPrimary, hElevatedToken);
  UserTokenElevated := TJwSecurityToken.CreateDuplicateExistingToken(hElevatedToken, MAXIMUM_ALLOWED);
  UserTokenElevated.ConvertToImpersonatedToken(SecurityImpersonation, MAXIMUM_ALLOWED);
  if UserInGroup(DOMAIN_ALIAS_RID_ADMINS, UserTokenElevated.TokenHandle) then
...

himitsu 28. Aug 2014 08:52

AW: Per ProcessId prüfen, ob der Benutzer Admin ist (oder: ProcessId -> UserTokenHand
 
Wirklich nicht klar?

Das UAC simuliert doch das, was die Leute sich weigerten zu machen => Das Arbeiten als Benutzer und nur Admin wenn nötig.
* Programme arbeiten standardmäßig mit "Benutzer"-Rechten
* und nur wenn nötig, werden nach Rückfrage die vollen Rechte einem Programm gewährt.

Somit sollte auch klar sein, daß die Token im Programm normalerweise nur den eingeschränkten Rechten entsprechen dürften, denn sonst könnte sich das Programm ja darüber mehr Rechte besorgen.

CodeX 28. Aug 2014 10:45

AW: Per ProcessId prüfen, ob der Benutzer Admin ist (oder: ProcessId -> UserTokenHand
 
Zitat:

Zitat von himitsu (Beitrag 1270089)
Somit sollte auch klar sein, daß die Token im Programm normalerweise nur den eingeschränkten Rechten entsprechen dürften

Es geht ja eben nicht um die Prüfung von Rechten, sondern von einer Gruppenzugehörigkeit. Das sind zwei verschiedene Paar Schuhe!
DOMAIN_ALIAS_RID_ADMINS fragt nach der Gruppe und eben nicht nach den zugehörigen Rechten. Ein Mitglied der Administratorgruppe muss nicht zwingend Administratorrechte haben - diese können über andere Richtlinien wieder eingeschränkt worden sein. Und umgekehrt.

Aber wenn das alles so klar ist, warum hast Du dann nicht vorher drauf hingewiesen? :?
Zudem wundere ich mich, dass zum Thema "Gruppenzugehörigkeit in Delphi prüfen" keine einzige Seite, auf der Codebeispiele zur Prüfung zu finden sind, auf diesen Umstand hinweist. Alle beschreiben schlicht die Prüfung des normalen UserTokens, was anscheinend seit Vista totaler Quatsch ist.

Luckie 28. Aug 2014 12:12

AW: Per ProcessId prüfen, ob der Benutzer Admin ist (oder: ProcessId -> UserTokenHand
 
Liste alle Mitglieder der Administratorengruppe auf und guck, ob ein Mitglied die SID des Benutzers hat, vom dem du wissen willst, ob er Admin-Privilegien hat.

himitsu 28. Aug 2014 12:42

AW: Per ProcessId prüfen, ob der Benutzer Admin ist (oder: ProcessId -> UserTokenHand
 
Des Benutzers, welcher grade den Desktop sieht (der, unter dessen Account die Session läuft), oder des Benutzers, welcher das Programm gestartet hat?

"Ausführen als ..."



Im Prinzip gibt es locker mal 4 Benutzertoken, von welchen man sich schon einen aussuchen sollte.
- der von der Session (ich vermute mal den will man hier wohl will)
- der UAC-Eingeschränkte, welchen Programme standardmäßig bekommen
- "Ausführen als ..."
- "Ausführen als ..." mit UAC-einschränkung


Dann kann man das gerne weitertreiben
- weitere "Ausführen als ..." von anderen Prozessen
- per Impersonate umgebogene Rechte einzelner Threads
- ...

CodeX 28. Aug 2014 12:47

AW: Per ProcessId prüfen, ob der Benutzer Admin ist (oder: ProcessId -> UserTokenHand
 
Mein Usecase steht doch eigentlich schon in der Überschrift: ProcessId -> UserTokenHandle
Da gibt es imho keine Mehrdeutigkeiten, oder? Eine ProcessId kann nur zu einem Benutzer gehören und von diesem möchte ich wissen, ob er sich in der Administratorgruppe befindet.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:38 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz