mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
FIX: Elevation does not work under standard user account (fixes #217)
This commit is contained in:
parent
4e3a509e8e
commit
ff7d514f15
4 changed files with 230 additions and 112 deletions
|
|
@ -985,10 +985,18 @@ begin
|
|||
Result:= False;
|
||||
end;
|
||||
|
||||
function SHGetValueW(hkey: HKEY; pszSubKey, pszValue: LPCWSTR; pdwType: PDWORD;
|
||||
pvData: LPVOID; pcbData: PDWORD): DWORD; stdcall; external 'shlwapi.dll';
|
||||
|
||||
function IsUserAdmin: TDuplicates;
|
||||
const
|
||||
SYSTEM = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System';
|
||||
var
|
||||
Success: Boolean;
|
||||
dwValue: DWORD = 0;
|
||||
ReturnLength: DWORD = 0;
|
||||
dwType: DWORD = REG_DWORD;
|
||||
dwValueSize: DWORD = SizeOf(DWORD);
|
||||
TokenHandle: HANDLE = INVALID_HANDLE_VALUE;
|
||||
TokenInformation: array [0..1023] of Byte;
|
||||
ElevationType: JwaVista.TTokenElevationType absolute TokenInformation;
|
||||
|
|
@ -1008,9 +1016,32 @@ begin
|
|||
if Success then
|
||||
begin
|
||||
case ElevationType of
|
||||
TokenElevationTypeDefault: Result:= dupIgnore; // The token does not have a linked token. (UAC disabled)
|
||||
TokenElevationTypeFull: Result:= dupAccept; // The token is an elevated token. (Administrator)
|
||||
TokenElevationTypeLimited: Result:= dupError; // The token is a limited token. (User)
|
||||
// The token is an elevated token. (Administrator)
|
||||
TokenElevationTypeFull: Result:= dupAccept;
|
||||
// The token is a limited token. (User)
|
||||
TokenElevationTypeLimited: Result:= dupError;
|
||||
// The token does not have a linked token. (UAC disabled or standard user)
|
||||
TokenElevationTypeDefault:
|
||||
begin
|
||||
if (SHGetValueW( HKEY_LOCAL_MACHINE, SYSTEM, 'EnableLUA',
|
||||
@dwType, @dwValue, @dwValueSize) <> ERROR_SUCCESS) then
|
||||
begin
|
||||
Exit(dupError);
|
||||
end;
|
||||
if (dwValue > 0) then
|
||||
begin
|
||||
if (SHGetValueW( HKEY_LOCAL_MACHINE, SYSTEM, 'ConsentPromptBehaviorUser',
|
||||
@dwType, @dwValue, @dwValueSize) <> ERROR_SUCCESS) then
|
||||
begin
|
||||
Exit(dupError);
|
||||
end;
|
||||
end;
|
||||
if (dwValue > 0) then
|
||||
Result:= dupError
|
||||
else begin
|
||||
Result:= dupIgnore;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
-------------------------------------------------------------------------
|
||||
Remote procedure call implementation (Windows)
|
||||
|
||||
Copyright (C) 2019 Alexander Koblov (alexx2000@mail.ru)
|
||||
Copyright (C) 2019-2021 Alexander Koblov (alexx2000@mail.ru)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -72,7 +72,8 @@ type
|
|||
implementation
|
||||
|
||||
uses
|
||||
JwaWinNT, JwaAclApi, JwaAccCtrl, JwaWinBase, Windows, uNamedPipes, uDebug;
|
||||
JwaWinNT, JwaAclApi, JwaAccCtrl, JwaWinBase, Windows, DCOSUtils,
|
||||
uNamedPipes, uDebug, uProcessInfo;
|
||||
|
||||
{ TPipeTransport }
|
||||
|
||||
|
|
@ -80,7 +81,7 @@ procedure TPipeTransport.Connect;
|
|||
begin
|
||||
if (FPipe = 0) then
|
||||
begin
|
||||
DCDebug('Connect to ', FAddress,'_');
|
||||
DCDebug('Connect to ', String(FAddress));
|
||||
|
||||
FPipe:= CreateFileW(PWideChar('\\.\pipe\' + FAddress),
|
||||
GENERIC_WRITE or
|
||||
|
|
@ -250,119 +251,183 @@ procedure TServerListnerThread.Execute;
|
|||
var
|
||||
SID: TBytes;
|
||||
dwWait: DWORD;
|
||||
hPipe: THandle;
|
||||
ACL: PACL = nil;
|
||||
hProcess: HANDLE;
|
||||
bPending: Boolean;
|
||||
cCount: ULONG = 1;
|
||||
SecondSID: TBytes;
|
||||
AName: UnicodeString;
|
||||
ReturnLength: DWORD = 0;
|
||||
Overlapped: TOverlapped;
|
||||
SA: TSecurityAttributes;
|
||||
SD: TSecurityDescriptor;
|
||||
TokenHandle: HANDLE = 0;
|
||||
Events: array[0..1] of THandle;
|
||||
ExplicitAccess: TExplicitAccess;
|
||||
hPipe: THandle = INVALID_HANDLE_VALUE;
|
||||
TokenInformation: array [0..1023] of Byte;
|
||||
ExplicitAccess: array [0..1] of TExplicitAccess;
|
||||
ElevationType: TTokenElevationType absolute TokenInformation;
|
||||
begin
|
||||
AName:= UTF8Decode(FOwner.Name);
|
||||
|
||||
if not GetCurrentUserSID(SID) then Halt;
|
||||
|
||||
ZeroMemory(@ExplicitAccess, SizeOf(ExplicitAccess));
|
||||
with ExplicitAccess do
|
||||
begin
|
||||
grfAccessPermissions:= GENERIC_ALL;
|
||||
grfAccessMode:= DWORD(SET_ACCESS);
|
||||
grfInheritance:= NO_INHERITANCE;
|
||||
Trustee.TrusteeForm:= DWORD(TRUSTEE_IS_SID);
|
||||
Trustee.TrusteeType:= DWORD(TRUSTEE_IS_USER);
|
||||
Trustee.ptstrName:= PAnsiChar(@SID[0]);
|
||||
if (FOwner.ProcessId > 0) then
|
||||
dwWait:= FOwner.ProcessId
|
||||
else begin
|
||||
dwWait:= System.GetProcessId;
|
||||
end;
|
||||
|
||||
if SetEntriesInAcl(1, @ExplicitAccess, nil, JwaWinNT.PACL(ACL)) <> ERROR_SUCCESS then
|
||||
Halt;
|
||||
if not InitializeSecurityDescriptor (@SD, SECURITY_DESCRIPTOR_REVISION) then
|
||||
Halt;
|
||||
if not SetSecurityDescriptorDacl(@SD, True, ACL, False) then
|
||||
Halt;
|
||||
try
|
||||
hProcess:= OpenProcess(PROCESS_QUERY_INFORMATION, False, dwWait);
|
||||
if hProcess = 0 then RaiseLastOSError;
|
||||
|
||||
ZeroMemory(@Overlapped, SizeOf(TOverlapped));
|
||||
ZeroMemory(@Overlapped, SizeOf(TOverlapped));
|
||||
try
|
||||
if not OpenProcessToken(hProcess, TOKEN_QUERY, TokenHandle) then
|
||||
RaiseLastOSError;
|
||||
|
||||
Overlapped.hEvent:= CreateEvent(nil, True, True, nil);
|
||||
if not GetTokenUserSID(TokenHandle, SID) then
|
||||
RaiseLastOSError;
|
||||
|
||||
Events[0]:= Overlapped.hEvent;
|
||||
Events[1]:= FEvent;
|
||||
|
||||
while not Terminated do
|
||||
begin
|
||||
SA.nLength := SizeOf(SA);
|
||||
SA.lpSecurityDescriptor := @SD;
|
||||
SA.bInheritHandle := True;
|
||||
|
||||
// Create pipe server
|
||||
hPipe := CreateNamedPipeW(PWideChar('\\.\pipe\' + AName),
|
||||
PIPE_ACCESS_DUPLEX or
|
||||
FILE_FLAG_OVERLAPPED,
|
||||
PIPE_WAIT or
|
||||
PIPE_READMODE_BYTE or
|
||||
PIPE_TYPE_BYTE,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
maxSmallint,
|
||||
maxSmallint,
|
||||
0,
|
||||
@SA);
|
||||
|
||||
if hPipe = INVALID_HANDLE_VALUE then
|
||||
Halt;
|
||||
|
||||
DCDebug('Start server ', FOwner.Name);
|
||||
|
||||
FReadyEvent.SetEvent;
|
||||
|
||||
while not Terminated do
|
||||
begin
|
||||
bPending:= False;
|
||||
|
||||
if not ConnectNamedPipe(hPipe, @Overlapped) then
|
||||
ZeroMemory(@ExplicitAccess, SizeOf(ExplicitAccess));
|
||||
with ExplicitAccess[0] do
|
||||
begin
|
||||
case (GetLastError()) of
|
||||
ERROR_IO_PENDING:
|
||||
bPending:= True;
|
||||
ERROR_PIPE_CONNECTED:
|
||||
SetEvent(Overlapped.hEvent);
|
||||
else
|
||||
grfAccessPermissions:= GENERIC_ALL;
|
||||
grfAccessMode:= DWORD(SET_ACCESS);
|
||||
grfInheritance:= NO_INHERITANCE;
|
||||
Trustee.TrusteeForm:= DWORD(TRUSTEE_IS_SID);
|
||||
Trustee.TrusteeType:= DWORD(TRUSTEE_IS_USER);
|
||||
Trustee.ptstrName:= PAnsiChar(@SID[0]);
|
||||
end;
|
||||
|
||||
if not GetTokenInformation(TokenHandle, TokenElevationType, @TokenInformation,
|
||||
SizeOf(TokenInformation), ReturnLength) then
|
||||
begin
|
||||
RaiseLastOSError;
|
||||
end;
|
||||
|
||||
if ElevationType = TokenElevationTypeDefault then
|
||||
begin
|
||||
with ExplicitAccess[1] do
|
||||
begin
|
||||
grfAccessPermissions:= GENERIC_ALL;
|
||||
grfAccessMode:= DWORD(SET_ACCESS);
|
||||
grfInheritance:= NO_INHERITANCE;
|
||||
Trustee.TrusteeForm:= DWORD(TRUSTEE_IS_SID);
|
||||
end;
|
||||
if (FOwner.ProcessId = 0) then
|
||||
begin
|
||||
if not GetAdministratorsSID(SecondSID) then
|
||||
RaiseLastOSError;
|
||||
ExplicitAccess[1].Trustee.TrusteeType:= DWORD(TRUSTEE_IS_GROUP);
|
||||
end
|
||||
else begin
|
||||
if not GetProcessUserSID(GetCurrentProcess, SecondSID) then
|
||||
RaiseLastOSError;
|
||||
ExplicitAccess[1].Trustee.TrusteeType:= DWORD(TRUSTEE_IS_USER);
|
||||
end;
|
||||
ExplicitAccess[1].Trustee.ptstrName:= PAnsiChar(@SecondSID[0]);
|
||||
cCount:= 2;
|
||||
end;
|
||||
|
||||
if SetEntriesInAcl(cCount, @ExplicitAccess[0], nil, JwaWinNT.PACL(ACL)) <> ERROR_SUCCESS then
|
||||
RaiseLastOSError;
|
||||
if not InitializeSecurityDescriptor (@SD, SECURITY_DESCRIPTOR_REVISION) then
|
||||
RaiseLastOSError;
|
||||
if not SetSecurityDescriptorDacl(@SD, True, ACL, False) then
|
||||
RaiseLastOSError;
|
||||
|
||||
Overlapped.hEvent:= CreateEvent(nil, True, True, nil);
|
||||
|
||||
Events[0]:= Overlapped.hEvent;
|
||||
Events[1]:= FEvent;
|
||||
|
||||
while not Terminated do
|
||||
begin
|
||||
SA.nLength := SizeOf(SA);
|
||||
SA.lpSecurityDescriptor := @SD;
|
||||
SA.bInheritHandle := True;
|
||||
|
||||
// Create pipe server
|
||||
hPipe := CreateNamedPipeW(PWideChar('\\.\pipe\' + AName),
|
||||
PIPE_ACCESS_DUPLEX or
|
||||
FILE_FLAG_OVERLAPPED,
|
||||
PIPE_WAIT or
|
||||
PIPE_READMODE_BYTE or
|
||||
PIPE_TYPE_BYTE,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
maxSmallint,
|
||||
maxSmallint,
|
||||
0,
|
||||
@SA);
|
||||
|
||||
if hPipe = INVALID_HANDLE_VALUE then
|
||||
RaiseLastOSError;
|
||||
|
||||
DCDebug('Start server ', FOwner.Name);
|
||||
|
||||
FReadyEvent.SetEvent;
|
||||
|
||||
while not Terminated do
|
||||
begin
|
||||
bPending:= False;
|
||||
|
||||
if not ConnectNamedPipe(hPipe, @Overlapped) then
|
||||
begin
|
||||
case (GetLastError()) of
|
||||
ERROR_IO_PENDING:
|
||||
bPending:= True;
|
||||
ERROR_PIPE_CONNECTED:
|
||||
SetEvent(Overlapped.hEvent);
|
||||
else
|
||||
begin
|
||||
DisconnectNamedPipe(hPipe);
|
||||
Continue;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Wait client connection
|
||||
dwWait := WaitForMultipleObjectsEx(Length(Events), Events, False, INFINITE, True);
|
||||
|
||||
if (dwWait = 1) or ((dwWait = 0) and bPending and (not GetOverlappedResult(hPipe, Overlapped, dwWait, False))) then
|
||||
begin
|
||||
DisconnectNamedPipe(hPipe);
|
||||
Continue;
|
||||
end;
|
||||
|
||||
if (FOwner.VerifyChild and not VerifyChild(hPipe)) or
|
||||
(FOwner.VerifyParent and not VerifyParent(hPipe)) then
|
||||
begin
|
||||
DisconnectNamedPipe(hPipe);
|
||||
Continue;
|
||||
end;
|
||||
|
||||
Break;
|
||||
end; // while
|
||||
|
||||
if not Terminated then
|
||||
TClientHandlerThread.Create(hPipe, FOwner)
|
||||
else begin
|
||||
DisconnectNamedPipe(hPipe);
|
||||
end;
|
||||
end;
|
||||
|
||||
// Wait client connection
|
||||
dwWait := WaitForMultipleObjectsEx(Length(Events), Events, False, INFINITE, True);
|
||||
|
||||
if (dwWait = 1) or ((dwWait = 0) and bPending and (not GetOverlappedResult(hPipe, Overlapped, dwWait, False))) then
|
||||
begin
|
||||
DisconnectNamedPipe(hPipe);
|
||||
Continue;
|
||||
end;
|
||||
|
||||
if (FOwner.VerifyChild and not VerifyChild(hPipe)) or
|
||||
(FOwner.VerifyParent and not VerifyParent(hPipe)) then
|
||||
begin
|
||||
DisconnectNamedPipe(hPipe);
|
||||
Continue;
|
||||
end;
|
||||
|
||||
Break;
|
||||
end; // while
|
||||
|
||||
if not Terminated then
|
||||
TClientHandlerThread.Create(hPipe, FOwner)
|
||||
else begin
|
||||
DisconnectNamedPipe(hPipe);
|
||||
end; // while
|
||||
finally
|
||||
CloseHandle(hProcess);
|
||||
if Assigned(ACL) then LocalFree(HLOCAL(ACL));
|
||||
if (TokenHandle > 0) then CloseHandle(TokenHandle);
|
||||
if (hPipe <> INVALID_HANDLE_VALUE) then CloseHandle(hPipe);
|
||||
if (Overlapped.hEvent > 0) then CloseHandle(Overlapped.hEvent);
|
||||
end;
|
||||
end; // while
|
||||
|
||||
CloseHandle(hPipe);
|
||||
CloseHandle(Events[0]);
|
||||
LocalFree(HLOCAL(ACL));
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
DCDebug(E.Message);
|
||||
if FOwner.ProcessId > 0 then
|
||||
Halt
|
||||
else
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
|
|||
|
|
@ -26,12 +26,12 @@ type
|
|||
|
||||
function VerifyChild(hPipe: THandle): Boolean;
|
||||
function VerifyParent(hPipe: THandle): Boolean;
|
||||
function GetCurrentUserSID(var SID: TBytes): Boolean;
|
||||
function GetAdministratorsSID(out SID: TBytes): Boolean;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
JwaWinNT, Windows, uProcessInfo, uDebug;
|
||||
Windows, uProcessInfo, uDebug;
|
||||
|
||||
var
|
||||
GetNamedPipeClientProcessId: function(Pipe: HANDLE; ClientProcessId: PULONG): BOOL; stdcall;
|
||||
|
|
@ -64,7 +64,6 @@ begin
|
|||
// Allow to connect from parent process and same executable only
|
||||
if ClientProcessId = GetParentProcessId(GetCurrentProcessId) then
|
||||
begin
|
||||
|
||||
DCDebug('My: ', GetProcessFileName(GetCurrentProcess));
|
||||
DCDebug('Client: ', GetProcessFileNameEx(ClientProcessId));
|
||||
|
||||
|
|
@ -75,29 +74,23 @@ begin
|
|||
Result:= False;
|
||||
end;
|
||||
|
||||
function GetCurrentUserSID(var SID: TBytes): Boolean;
|
||||
function GetAdministratorsSID(out SID: TBytes): Boolean;
|
||||
const
|
||||
SECURITY_NT_AUTHORITY: TSidIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5));
|
||||
var
|
||||
ReturnLength: DWORD = 0;
|
||||
TokenHandle: HANDLE = INVALID_HANDLE_VALUE;
|
||||
TokenInformation: array [0..SECURITY_MAX_SID_SIZE] of Byte;
|
||||
UserToken: TTokenUser absolute TokenInformation;
|
||||
AdministratorsGroup: PSID = nil;
|
||||
begin
|
||||
Result:= OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, TokenHandle);
|
||||
if not Result then
|
||||
begin
|
||||
if GetLastError = ERROR_NO_TOKEN then
|
||||
Result:= OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, TokenHandle);
|
||||
end;
|
||||
Result:= AllocateAndInitializeSid(SECURITY_NT_AUTHORITY,
|
||||
2,
|
||||
SECURITY_BUILTIN_DOMAIN_RID,
|
||||
DOMAIN_ALIAS_RID_ADMINS,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
AdministratorsGroup);
|
||||
if Result then
|
||||
begin
|
||||
Result:= GetTokenInformation(TokenHandle, TokenUser,
|
||||
@TokenInformation, SizeOf(TokenInformation), ReturnLength);
|
||||
CloseHandle(TokenHandle);
|
||||
if Result then
|
||||
begin
|
||||
SetLength(SID, GetLengthSid(UserToken.User.Sid));
|
||||
CopySid(Length(SID), PSID(@SID[0]), UserToken.User.Sid);
|
||||
end;
|
||||
SetLength(SID, GetLengthSid(AdministratorsGroup));
|
||||
CopySid(Length(SID), PSID(@SID[0]), AdministratorsGroup);
|
||||
FreeSid(AdministratorsGroup);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ unit uProcessInfo;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Windows;
|
||||
Classes, SysUtils, JwaWinNT, Windows;
|
||||
|
||||
function GetParentProcessId(ProcessId: DWORD): DWORD;
|
||||
function GetProcessFileName(hProcess: HANDLE): UnicodeString;
|
||||
function GetProcessFileNameEx(ProcessId: DWORD): UnicodeString;
|
||||
function GetTokenUserSID(hToken: HANDLE; out SID: TBytes): Boolean;
|
||||
function GetProcessUserSID(hProcess: HANDLE; out SID: TBytes): Boolean;
|
||||
|
||||
implementation
|
||||
|
||||
|
|
@ -66,6 +68,33 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function GetTokenUserSID(hToken: HANDLE; out SID: TBytes): Boolean;
|
||||
var
|
||||
ReturnLength: DWORD = 0;
|
||||
TokenInformation: array [0..SECURITY_MAX_SID_SIZE] of Byte;
|
||||
UserToken: TTokenUser absolute TokenInformation;
|
||||
begin
|
||||
Result:= GetTokenInformation(hToken, TokenUser, @TokenInformation,
|
||||
SizeOf(TokenInformation), ReturnLength);
|
||||
if Result then
|
||||
begin
|
||||
SetLength(SID, GetLengthSid(UserToken.User.Sid));
|
||||
CopySid(Length(SID), PSID(@SID[0]), UserToken.User.Sid);
|
||||
end;
|
||||
end;
|
||||
|
||||
function GetProcessUserSID(hProcess: HANDLE; out SID: TBytes): Boolean;
|
||||
var
|
||||
hToken: HANDLE = 0;
|
||||
begin
|
||||
Result:= OpenProcessToken(hProcess, TOKEN_QUERY, hToken);
|
||||
if Result then
|
||||
begin
|
||||
Result:= GetTokenUserSID(hToken, SID);
|
||||
CloseHandle(hToken);
|
||||
end;
|
||||
end;
|
||||
|
||||
initialization
|
||||
Pointer(GetProcessImageFileNameW):= GetProcAddress(GetModuleHandle('psapi.dll'),
|
||||
'GetProcessImageFileNameW');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue