![]() |
Administratorrechte anfordern (Elevation/Impersonation ?)
Moin,
folgendes Szenario. Ich habe einen Dienst, nennen wir ihn Dienst.exe. Zum Installieren oder Deinstallieren benötigt man Administratorrechte (oder nicht?). Das ist bis hierhin unter Windows XP oder 2003 nicht weiter problematisch, unter Vista hingegen startet man bekanntermaßen ein Programm auch dann ohne Administratorrechte, wenn man als Administrator angemeldet ist. Wenn jetzt also ein Benutzer
Code:
aufruft, und nur dann, soll das Programm als Administrator ausgeführt werden. Vielleicht habe ich hier und in Google nur die falschen Suchworte benutzt, aber ich habe bislang nur die folgende Möglichkeit gefunden:
Dienst.exe -install
bzw. Dienst.exe -uninstall Über ein Vista-Manifest highestAvailablePrivileges anfordern. Das bedeutet, dass das Programm, von einem Benutzer gestartet, von diesem Administratorrechte anfordert, ist das richtig? Also als Administrator muss man einmal auf Bestätigen klicken, als normaler Benutzer muss man Benutzername/Passwort eingeben? Oder startet das Programm dann als normaler Benutzer gar nicht oder mit niedrigeren Rechten? Und was passiert nun, wenn der SCM das Programm als Dienst starten möchte? Hat es dann eventuell höhere Rechte als notwendig (SYSTEM-Account)? Mir war so, als hätte ich auch von der Möglichkeit gelesen, einem Dienst so wenig Rechte wie möglich (aber so viel wie nötig) zuzuweisen. Gibt es alternativ die Möglichkeit, die Rechte erst später anzufordern, nämlich dann, wenn sie benötigt werden, und das am besten so, dass es unter XP auch funktioniert (wenn das dort überhaupt möglich ist)? Das wäre mir nämlich eigentlich lieber. Ich habe von dem Stichwort Impersonation gehört, aber konnte nicht recht eine schöne Erklärung dazu finden, bzw. ob das der richtige Weg ist. Sachdienliche Hinweise werden mit Freude entgegengenommen, auch, ob eventuell eine komplett andere Vorgehensweise günstiger ist. |
Re: Administratorrechte anfordern (Elevation/Impersonation ?
Hmm, ausnahmsweise pushe ich mal meinen Beitrag - da muss es doch jemanden geben, der sich damit auskennt :stupid:
|
Re: Administratorrechte anfordern (Elevation/Impersonation ?
Zitat:
Zitat:
Ich sehe also keinen Grund, warum das 'Programm' von einem unterpriviligierten Benutzer verwendet werden sollte. Zitat:
Admin = kein UAC-Dialog, eingeschränktes Token Users = kein UAC-Dialog [highestAvailable] Admin = UAC-Dialog, komplettes Token Users = kein UAC-Dialog [requireAdministrator] Admin = UAC-Dialog, komplettes Token Users = UAC-Dialog mit Username/Passwort für Admin-Account requireAdministrator wäre wohl das gesuchte. Zitat:
Zitat:
|
Re: Administratorrechte anfordern (Elevation/Impersonation ?
Danke, das hilft mir schon sehr weiter. Vermutlich hast du recht, eigentlich besteht keine Notwendigkeit, das Programm von einem normalen Benutzer ausführen zu lassen. Was mir noch nicht ganz klar ist: Ich bekomme also mit einem requireAdministrator im Manifest unter Vista das gewünschte Verhalten - gibt es eine direkt vergleichbare Möglichkeit unter XP, oder muss dort der Benutzer schon vorher als Administrator angemeldet sein bzw. das Programm explizit als Administrator ausführen? Vermutlich ist das nicht so wichtig, weil es keine Katastrophe wäre, zum Administrieren eines Rechners als Administrator eingeloggt zu sein, aber man weiß ja nie...
|
Re: Administratorrechte anfordern (Elevation/Impersonation ?
Zitat:
ps: das hat aber eher mit Benutzerfreundlichkeit zu tun, die Installation als nicht-Admin würde ohnehin fehlschlagen. |
Re: Administratorrechte anfordern (Elevation/Impersonation ?
So, jetzt habe ich einfach mal das Manifest eingebunden und das funktioniert soweit. Also PowerShell geöffnet, Dienst.exe -install aufgerufen, UAC meldet sich mit einem nichtidentifizierten Programm (das wird erst richtig schön, wenn es signiert ist, oder?), ich bestätige... und es öffnet sich eine neue Konsole, gibt was aus und schließt die Konsole wieder. Plöd, man sieht ja gar nicht, was das Ding ausgibt, weil es zu schnell wieder zu ist und überhaupt lieber in die ursprüngliche Shell gehört. Ich schätze, da gibt es keine schöne Möglichkeit drumherum, außer indem es sich mit runas noch einmal selbst startet, oder? Aber ich dachte, um genau so eine Notwendigkeit zu vermeiden, gibt es jetzt diese Manifest-UAC-Geschichte...^^
Edit: Gerade gelesen unter IsUserAnAdmin(): Zitat:
|
Re: Administratorrechte anfordern (Elevation/Impersonation ?
Zitat:
Ich schau später mal, ob ich meine Wrapper-Funktionen noch finde :) |
Re: Administratorrechte anfordern (Elevation/Impersonation ?
Meinst du die:
Delphi-Quellcode:
////////////////////////////////////////////////////////////////////////////////
// Procedure : GetAdminSid // Author : NBe // Comment : function GetAdminSid: PSID; const // bekannte SIDs ... (WinNT.h) SECURITY_NT_AUTHORITY: TSIDIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5)); // bekannte RIDs ... (WinNT.h) SECURITY_BUILTIN_DOMAIN_RID: DWORD = $00000020; DOMAIN_ALIAS_RID_ADMINS: DWORD = $00000220; begin Result := nil; AllocateAndInitializeSid(SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, Result); end; //////////////////////////////////////////////////////////////////////////////// // Procedure : IsAdmin // Author : NBe // Comment : function IsAdmin: LongBool; var TokenHandle : THandle; ReturnLength : DWORD; TokenInformation : PTokenGroups; AdminSid : PSID; Loop : Integer; begin Result := False; TokenHandle := 0; if OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, TokenHandle) then try ReturnLength := 0; GetTokenInformation(TokenHandle, TokenGroups, nil, 0, ReturnLength); TokenInformation := GetMemory(ReturnLength); if Assigned(TokenInformation) then try if GetTokenInformation(TokenHandle, TokenGroups, TokenInformation, ReturnLength, ReturnLength) then begin AdminSid := GetAdminSid; for Loop := 0 to TokenInformation^.GroupCount - 1 do begin if EqualSid(TokenInformation^.Groups[Loop].Sid, AdminSid) then begin Result := True; Break; end; end; FreeSid(AdminSid); end; finally FreeMemory(TokenInformation); end; finally CloseHandle(TokenHandle); end; end; |
Re: Administratorrechte anfordern (Elevation/Impersonation ?
Klingt toll ^^
Ich hab das mit ShellExecute und so jetzt mal getestet - das Programm mit Administratorrechten wird dann ja trotzdem nicht in derselben Konsole gestartet, wie unangenehm. Nun gibt es natürlich nicht viele Ausgaben im Normalfall, aber im Fehlerfall eben doch. Bei der ShellExecute-Geschichte seh ich gar keine Ausgabe, bei der requireAdministrator-Geschichte ist sie in einer eigenen Konsole, die sich sofort wieder schließt, da hast du nicht zufällig auch noch nen Tipp? In einer bereits als Administrator gestarteten PowerShell bekomme ich alle Ausgaben. Und dass ich beim Linken die Warnung
Code:
bekomme, klingt so, als ob da noch was nicht stimmt, aber Vista liest das Ding trotzdem richtig aus.
1>.\VistaAdmin.manifest : manifest authoring warning 81010002: Unrecognized Element "requestedPrivileges" in namespace "urn:schemas-microsoft-com:asm.v3".
Huch, roter Kasten, danke für den Code, Luckie. Damit kann ich schonmal gezielter Fehlermeldungen ausgeben als auf ERROR_ACCESS_DENIED zu warten. Aber irgendwie schmeckt mir die ganze Geschichte mit entweder fehlenden bzw. an der falschen Stelle stehenden Ausgaben, oder alternativ ohne UAC nicht so richtig. Naja, mal schauen. Edit: Ich habe den Code jetzt ausprobiert. Wenn ich das richtig sehe, gibt der Code ja nur an, ob man in der Gruppe Administratoren ist... Nicht, ob man Admin-Rechte hat. Da muss ich wohl noch etwas suchen, oder hoffen, dass NicoDE seine Wrapper-Funktionen findet ;) Und noch ein Edit: In Absprache mit meinem Kollegen ist es wohl besser, wenn wir auf UAC verzichten und stattdessen bei fehlenden Rechten nur eine Fehlermeldung ausgeben, weil man mit UAC ja eventuelle Fehlermeldungen nicht oder nur ungünstig lesen kann. Also wäre eine IsAdmin-Funktion inklusive Vista-Unterstützung das Beste (wobei ich zurzeit einfach schaue, ob das Öffnen des SCM mit ERROR_ACCESS_DENIED fehlschlägt, nicht schön, aber reicht im Endeffekt erstmal). |
Re: Administratorrechte anfordern (Elevation/Impersonation ?
Zitat:
(sind genauso zu verwenden wie die API-Funktionen - die Fallbacks dürften auch das gleiche machen wie die jeweilige API *g*)
Delphi-Quellcode:
ps: Die von Luckie kopierte IsAdmin-Funktion enthält den klassischen Fehler (es wurde vergessen zu prüfen, ob die aktuelle Sid überhaupt im Token aktiviert ist).
unit TokenTools;
interface uses Windows; function CheckTokenMembership(TokenHandle: THandle; SidToCheck: PSID; out IsMember: BOOL): BOOL; stdcall; function SHTestTokenMembership(hToken: THandle; ulRID: ULONG): BOOL; stdcall; function IsUserAnAdmin(): BOOL; stdcall; implementation function GetAdvApi32Lib(): HMODULE; const ModuleName = 'ADVAPI32'; {$WRITEABLECONST ON} const ModuleHandle: HMODULE = HMODULE(nil); {$WRITEABLECONST OFF} begin Result := ModuleHandle; if Result = HMODULE(nil) then begin Result := LoadLibrary(ModuleName); if Result <> HMODULE(nil) then ModuleHandle := Result; end; end; function CheckTokenMembership(TokenHandle: THandle; SidToCheck: PSID; out IsMember: BOOL): BOOL; type TFNCheckTokenMembership = function(TokenHandle: THandle; SidToCheck: PSID; out IsMember: BOOL): BOOL; stdcall; {$WRITEABLECONST ON} const Initialized: Integer = 0; RealApiFunc: TFNCheckTokenMembership = nil; {$WRITEABLECONST OFF} type TAceHeader = packed record AceType : Byte; AceFlags: Byte; AceSize : Word; end; TAccessAllowedAce = packed record Header : TAceHeader; Mask : ACCESS_MASK; SidStart: DWORD; end; const ACL_REVISION = 2; DesiredAccess = 1; GenericMapping: TGenericMapping = ( GenericRead : STANDARD_RIGHTS_READ; GenericWrite : STANDARD_RIGHTS_WRITE; GenericExecute: STANDARD_RIGHTS_EXECUTE; GenericAll : STANDARD_RIGHTS_ALL ); var ClientToken: THandle; ProcessToken: THandle; SecurityDescriptorSize: Cardinal; SecurityDescriptor: PSecurityDescriptor; Dacl: PACL; PrivilegeSetBufferSize: ULONG; PrivilegeSetBuffer: packed record PrivilegeSet: TPrivilegeSet; Buffer: array [0..2] of TLUIDAndAttributes; end; GrantedAccess: ACCESS_MASK; AccessStatus: BOOL; begin if Initialized = 0 then begin RealApiFunc := TFNCheckTokenMembership( GetProcAddress(GetAdvApi32Lib(), 'CheckTokenMembership')); InterlockedIncrement(Initialized); end; if Assigned(RealApiFunc) then Result := RealApiFunc(TokenHandle, SidToCheck, IsMember) else begin Result := False; IsMember := False; ClientToken := THandle(nil); try if TokenHandle <> THandle(nil) then ClientToken := TokenHandle else if not OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, False, ClientToken) then begin ClientToken := THandle(nil); if GetLastError() = ERROR_NO_TOKEN then begin if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY or TOKEN_DUPLICATE, ProcessToken) then try if not DuplicateToken(ProcessToken, SecurityImpersonation, @ClientToken) then begin ClientToken := THandle(nil); end; finally CloseHandle(ProcessToken); end; end; end; if ClientToken <> THandle(nil) then begin SecurityDescriptorSize := SizeOf(TSecurityDescriptor) + SizeOf(TAccessAllowedAce) + SizeOf(TACL) + 3 * GetLengthSid(SidToCheck); SecurityDescriptor := PSecurityDescriptor( LocalAlloc(LMEM_ZEROINIT, SecurityDescriptorSize)); if SecurityDescriptor <> nil then try if InitializeSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION) then begin if SetSecurityDescriptorOwner(SecurityDescriptor, SidToCheck, False) then begin if SetSecurityDescriptorGroup(SecurityDescriptor, SidToCheck, False) then begin Dacl := PACL(SecurityDescriptor); Inc(PSecurityDescriptor(Dacl)); if InitializeAcl(Dacl^, SecurityDescriptorSize - SizeOf(TSecurityDescriptor), ACL_REVISION) then begin if AddAccessAllowedAce(Dacl^, ACL_REVISION, DesiredAccess, SidToCheck) then begin if SetSecurityDescriptorDacl(SecurityDescriptor, True, Dacl, False) then begin PrivilegeSetBufferSize := SizeOf(PrivilegeSetBuffer); Result := AccessCheck(SecurityDescriptor, ClientToken, DesiredAccess, GenericMapping, PrivilegeSetBuffer.PrivilegeSet, PrivilegeSetBufferSize, GrantedAccess, AccessStatus); if Result then IsMember := AccessStatus and (GrantedAccess = DesiredAccess); end; end; end; end; end; end; finally LocalFree(HLOCAL(SecurityDescriptor)); end; end; finally if (ClientToken <> THandle(nil)) and (ClientToken <> TokenHandle) then begin CloseHandle(ClientToken); end; end; end; end; function GetShell32Lib(): HMODULE; const ModuleName = 'SHELL32'; {$WRITEABLECONST ON} const ModuleHandle: HMODULE = HMODULE(nil); {$WRITEABLECONST OFF} begin Result := ModuleHandle; if Result = HMODULE(nil) then begin Result := LoadLibrary(ModuleName); if Result <> HMODULE(nil) then ModuleHandle := Result; end; end; function SHTestTokenMembership(hToken: THandle; ulRID: ULONG): BOOL; stdcall; type TFNSHTestTokenMembership = function(hToken: THandle; ulRID: ULONG): BOOL; stdcall; {$WRITEABLECONST ON} const Initialized: Integer = 0; RealApiFunc: TFNSHTestTokenMembership = nil; {$WRITEABLECONST OFF} const SECURITY_NT_AUTHORITY: TSIDIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5)); SECURITY_BUILTIN_DOMAIN_RID = $00000020; var SidToCheck: PSID; begin if Initialized = 0 then begin RealApiFunc := TFNSHTestTokenMembership( GetProcAddress(GetShell32Lib(), 'SHTestTokenMembership')); InterlockedIncrement(Initialized); end; if Assigned(RealApiFunc) then Result := RealApiFunc(hToken, ulRID) else begin Result := AllocateAndInitializeSid(SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID, ulRID, 0, 0, 0, 0, 0, 0, SidToCheck); if Result then try if not CheckTokenMembership(THandle(nil), SidToCheck, Result) then Result := False; finally FreeSid(SidToCheck); end; end; end; function IsUserAnAdmin(): BOOL; const DOMAIN_ALIAS_RID_ADMINS = $00000220; type TFNIsUserAnAdmin = function(): BOOL; stdcall; {$WRITEABLECONST ON} const Initialized: Integer = 0; RealApiFunc: TFNIsUserAnAdmin = nil; {$WRITEABLECONST OFF} begin if Initialized = 0 then begin RealApiFunc := TFNIsUserAnAdmin( GetProcAddress(GetShell32Lib(), 'IsUserAnAdmin')); InterlockedIncrement(Initialized); end; if Assigned(RealApiFunc) then Result := RealApiFunc() else Result := SHTestTokenMembership(THandle(nil), DOMAIN_ALIAS_RID_ADMINS); end; end. pps: upz, da war aber noch nen blöder Fehler drin *g* |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:22 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz