Mustafa E. Korkmaz 0 Posted Friday at 12:33 PM (edited) I log in to Windows 11 with a username that does not have admin privileges. My program knows the admin username and password. When a button is pressed in the program, I want it to work again with admin authority. The person using the program should not be asked for the admin username and password. How can I do it? function RunAsAdmin(const Path, Params: string): Boolean; var sei: TShellExecuteInfo; begin try FillChar(sei, SizeOf(sei), 0); sei.cbSize := SizeOf(sei); sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI; sei.lpVerb := PChar('runas'); sei.lpFile := PChar(ExtractFileName(Path)); sei.lpDirectory := PChar(ExtractFilePath(Path)); sei.lpParameters := PChar(Params); sei.nShow := SW_SHOWNORMAL; Result := ShellExecuteEx(@sei); except result := false; end; end; procedure TForm1.Button1Click(Sender: TObject); begin RunAsAdmin( application.ExeName, '' ); end; When I use the ShellExecuteEx function Windows asks for the admin username and password. When these are entered, the program runs with admin authority. But the program already knows the admin username and password. I want it to run with admin permission without having to enter it again. function RunAs2( User, Password, commandline: String ) : integer; var StrInf : TStartupInfo; PrcInf : TProcessInformation; begin result := 1; try FillChar(StrInf,SizeOf(TStartupInfo),0); FillChar(PrcInf,SizeOf(TProcessInformation),0); StrInf.cb := SizeOf(TStartupInfo); StrInf.wShowWindow := SW_SHOWNORMAL; if CreateProcessWithLogon( PWideChar( WideString( User ) ), nil, PWideChar( WideString( Password ) ), 1, nil, PWideChar( WideString( commandline ) ), CREATE_UNICODE_ENVIRONMENT,//0, nil, nil, StrInf, PrcInf ) then result := 0; if Result = 0 then begin CloseHandle(PrcInf.hProcess); CloseHandle(PrcInf.hThread); end else result := GetLastError; except end; end; procedure TForm1.Button1Click(Sender: TObject); begin Showmessage( inttostr( RunAs2( 'User', 'test123', application.ExeName ) ) ) end; When I use the CreateProcessWithLogon function the following error is received: 5 ---> access denied For example, after connecting to the unauthorized computer remotely in one of the remote desktop applications, you send the remote admin username and password from the actions menu. The UAC dialog appears on the remote computer. If the user selects Yes, the application runs again with admin authority without being asked for the admin username and password. Edited Friday at 12:38 PM by Mustafa E. Korkmaz Share this post Link to post
FPiette 392 Posted Friday at 01:15 PM I have this code which could help you : {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Author: François PIETTE @ OverByte Creation: July 22, 2021 Description: Classes to act (File access or other) using another user account. License: This program is published under MOZILLA PUBLIC LICENSE V2.0; you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.mozilla.org/en-US/MPL/2.0/ Version: 1.0 History: Jul 22, 2021 1.00 F. Piette Initial release * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} unit ImpersonateUser; interface uses Winapi.Windows, System.Classes, System.SysUtils; const LOGON32_LOGON_NEW_CREDENTIALS = 9; // Missing in Delphi 10.4.2 type TImpersonateUser = class(TComponent) protected FUserToken : THandle; FErrorCode : DWORD; public destructor Destroy; override; function Logon(const UserName : String; const Domain : String; const Password : String) : Boolean; procedure Logoff(); property ErrorCode : DWORD read FErrorCode; end; implementation { TImpersonateUser } destructor TImpersonateUser.Destroy; begin if FUserToken <> 0 then begin CloseHandle(FUserToken); FUserToken := 0; end; inherited Destroy; end; procedure TImpersonateUser.Logoff; begin if FUserToken <> 0 then begin RevertToSelf(); // Revert to our user CloseHandle(FUserToken); FUserToken := 0; end; end; function TImpersonateUser.Logon( const UserName : String; const Domain : String; const Password : String): Boolean; var LoggedOn : Boolean; begin Result := FALSE; if FUserToken <> 0 then Logoff(); if UserName = '' then begin // Must at least provide a user name FErrorCode := ERROR_BAD_ARGUMENTS; Exit; end; if Domain <> '' then LoggedOn := LogonUser(PChar(UserName), PChar(Domain), PChar(Password), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, FUserToken) else LoggedOn := LogonUser(PChar(UserName), PChar(Domain), PChar(Password), LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, FUserToken); if not LoggedOn then begin FErrorCode := GetLastError(); Exit; end; if not ImpersonateLoggedOnUser(FUserToken) then begin FErrorCode := GetLastError(); Exit; end; FErrorCode := ERROR_SUCCESS; Result := TRUE; end; end. Once your code has called Logon(), it act a the user specified by the credentials. When finished, call Logoff(). Share this post Link to post
Mustafa E. Korkmaz 0 Posted Friday at 02:09 PM (edited) I tested the component you sent as follows. It still gave error number 5. The logon function is successful but I get an error in the next step. function RunAs(User, Password, commandline: String; hToken:THandle): Integer; var dwSize: DWORD; lpvEnv: Pointer; pi: TProcessInformation; si: TStartupInfo; szPath: Array [0..MAX_PATH] of WideChar; begin try ZeroMemory(@szPath, SizeOf(szPath)); ZeroMemory(@pi, SizeOf(pi)); ZeroMemory(@si, SizeOf(si)); si.cb:=SizeOf(TStartupInfo); try if CreateEnvironmentBlock(lpvEnv, hToken, True) then begin try dwSize:=SizeOf(szPath) div SizeOf(WCHAR); if (GetCurrentDirectoryW(dwSize, @szPath) > 0) then begin if (CreateProcessWithLogon(PWideChar(WideString(User)), nil, PWideChar(WideString(Password)), LOGON_WITH_PROFILE, nil, PWideChar(WideString(commandline)), CREATE_UNICODE_ENVIRONMENT, lpvEnv, szPath, si, pi)) then begin result:=ERROR_SUCCESS; CloseHandle(pi.hProcess); CloseHandle(pi.hThread); end else result:=GetLastError; end else result:=GetLastError; finally DestroyEnvironmentBlock(lpvEnv); end; end else result:=GetLastError; finally CloseHandle(hToken); end; except result := 1; end; end; procedure TForm1.FormCreate(Sender: TObject); begin ImpersonateUser := TImpersonateUser.Create( self ); end; procedure TForm1.Button1Click(Sender: TObject); begin if ImpersonateUser.Logon( 'User', '.', 'test123' ) then begin Showmessage( inttostr( RunAs( 'User', 'test123', application.ExeName, ImpersonateUser.FUserToken ) ) ); //------> 5 end; end; Edited Friday at 02:09 PM by Mustafa E. Korkmaz Share this post Link to post
DelphiUdIT 224 Posted Friday at 02:29 PM (edited) Why you use '.' for domain in the LogOn function? In the RunAs function you should use CreateProcessAsUser() API, not the CreateProcessWithLogon() .... you don't need to pass the credentials to RunAs, only the token. Edited Friday at 02:34 PM by DelphiUdIT Share this post Link to post
Mustafa E. Korkmaz 0 Posted Friday at 02:57 PM (edited) CreateProcessAsUser gives this error: 1314 "A required privilige is not held by the client" function RunAsXX(commandline: String; hToken:THandle): Integer; var dwSize: DWORD; lpvEnv: Pointer; pi: TProcessInformation; si: TStartupInfo; szPath: Array [0..MAX_PATH] of WideChar; begin try ZeroMemory(@szPath, SizeOf(szPath)); ZeroMemory(@pi, SizeOf(pi)); ZeroMemory(@si, SizeOf(si)); si.cb:=SizeOf(TStartupInfo); try if CreateEnvironmentBlock(lpvEnv, hToken, True) then begin try dwSize:=SizeOf(szPath) div SizeOf(WCHAR); if (GetCurrentDirectoryW(dwSize, @szPath) > 0) then begin if CreateProcessAsUser(hToken, nil, PWideChar(WideString(commandline)), nil, nil, FALSE, CREATE_UNICODE_ENVIRONMENT, lpvEnv, nil, si, pi) then begin result:=ERROR_SUCCESS; CloseHandle(pi.hProcess); CloseHandle(pi.hThread); end else result:=GetLastError; end else result:=GetLastError; finally DestroyEnvironmentBlock(lpvEnv); end; end else result:=GetLastError; finally CloseHandle(hToken); end; except result := 1; end; end; procedure TForm1.Button1Click(Sender: TObject); begin if ImpersonateUser.Logon( 'User', '', 'test123' ) then begin Showmessage( inttostr( RunAsXX( application.ExeName, ImpersonateUser.FUserToken ) ) ); end; end; Edited Friday at 02:58 PM by Mustafa E. Korkmaz Share this post Link to post
FPiette 392 Posted Friday at 06:59 PM 4 hours ago, Mustafa E. Korkmaz said: I tested the component you sent as follows. It still gave error number 5. The logon function is successful but I get an error in the next step. I the next step your app *has* the administrator privileges. Just run the external process as any user using only CreateProcess(). 1 Share this post Link to post
Remy Lebeau 1572 Posted Friday at 09:06 PM (edited) Running a process as an (impersonated) admin user, and running a process in an elevated state, are two different things. Being an admin user does not imply automatic elevation, but an elevation prompt does require an admin user. In any case, perhaps have a look at the CreateProcessWithLogonElevatedW() and CreateProcessWithTokenElevatedW() functions provided in the Elevate DLL of this old CodeProject article: Vista UAC: The Definitive Guide (I think the site is down undergoing a redesign at the moment, though. Maybe you can find another copy of the DLL elsewhere). Edited Friday at 09:06 PM by Remy Lebeau Share this post Link to post
Kas Ob. 135 Posted 23 hours ago 17 hours ago, Mustafa E. Korkmaz said: CreateProcessAsUser gives this error: 1314 "A required privilige is not held by the client" As Francois pointed, your user itself doesn't privilege to execute such operation, which is in this case CreateProcess(xx) with different token on different station, not %100 sure but i think your user should be have SeAssignPrimaryTokenPrivilege https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/security-policy-settings/user-rights-assignment https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/security-policy-settings/replace-a-process-level-token Share this post Link to post