V
V
vista1x2014-12-27 20:18:24
Delphi
vista1x, 2014-12-27 20:18:24

How to force the service to add an entry to the database on behalf of another user?

Hello! I will try to describe in detail the whole essence of the problem:
Under Win8 there are two users, user1 (administrator) and user2 (ordinary user). There is a service running from user1. What she does? It receives some data via sockets and launches another program (proga.exe) on behalf of user2 with parameters received from sockets. Next, proga.exe creates a file with the SHELL script in a certain directory and runs it. What the script does: creates some txt files (this succeeds) and adds some data to the Access DB via odbc (this fails).
Moreover, entries in the database are not written only if proga.exe is launched from the service. If the finished script is run from under user2, then the data is written.
From the service I launch proga.exe with the RunAs procedure, the description is below

procedure StrResetLength(var S: AnsiString);
begin
  SetLength(S, StrLen(PChar(S)));
end;

function GetUserObjectName(hUserObject: THandle): string;
var
  Count: DWORD;
begin
  // have the the API function determine the required string length
  GetUserObjectInformation(hUserObject, UOI_NAME, PChar(Result), 0, Count);
  SetLength(Result, Count + 1);

  if GetUserObjectInformation(hUserObject, UOI_NAME, PChar(Result), Count, Count) then
    StrResetLength(Result)
  else
    Result := '';
end;

function SetUserObjectFullAccess(hUserObject: THandle): Boolean;
var
  Sd: PSecurity_Descriptor;
  Si: Security_Information;
begin
  Sd := PSecurity_Descriptor(LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));
  InitializeSecurityDescriptor(Sd, SECURITY_DESCRIPTOR_REVISION);
  SetSecurityDescriptorDacl(Sd, True, nil, False);

  Si := DACL_SECURITY_INFORMATION;
  Result := SetUserObjectSecurity(hUserObject, Si, Sd);

  LocalFree(HLOCAL(Sd));
end;

procedure RunAs(var UserDomain, UserName, Password, CommandLine: string);
const
  Environment: PChar=nil;
  // default values for window stations and desktops
  CreateProcDEFWINSTATION = 'WinSta0';
  CreateProcDEFDESKTOP    = 'Default';
  CreateProcDOMUSERSEP    = '\';
var
  ConsoleTitle: string;
  Help: string;
  WinStaName: string;
  DesktopName: string;
  
  hUserToken: THandle;
  hWindowStation: HWINSTA;
  hDesktop: HDESK;
  StartUpInfo: TStartUpInfo;
  ProcInfo: TProcessInformation;
  Runned: boolean;
begin
  // Step 2: logon as the specified user
  if not LogonUser(PChar(UserName), PChar(UserDomain), PChar(Password),
                   LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hUserToken) then
    log('ERROR LogonUser');

  // Step 3: give the new user access to the current WindowStation and Desktop
  hWindowStation:= GetProcessWindowStation;
  WinStaName := GetUserObjectName(hWindowStation);
  if WinStaName = '' then
    WinStaName := CreateProcDEFWINSTATION;

  if not SetUserObjectFullAccess(hWindowStation) then begin
    CloseHandle(hUserToken);
   // raise EJclCreateProcessError.CreateResRecFmt(@RsCreateProcSetStationSecurityError, [WinStaName]);
    log('ERROR SetUserObjectFullAccess(hWindowStation');
  end;

  hDesktop := GetThreadDesktop(GetCurrentThreadId);
  DesktopName := GetUserObjectName(hDesktop);
  if DesktopName = '' then
    DesktopName := CreateProcDEFDESKTOP;

  if not SetUserObjectFullAccess(hDesktop) then begin
    CloseHandle(hUserToken);
   // raise EJclCreateProcessError.CreateResRecFmt(@RsCreateProcSetDesktopSecurityError, [DesktopName]);
    log('ERROR SetUserObjectFullAccess(hDesktop)');
  end;

  // Step 4: set the startup info for the new process
  ConsoleTitle := UserDomain + UserName;
  FillChar(StartUpInfo, SizeOf(StartUpInfo), #0);
  with StartUpInfo do begin
    cb:= SizeOf(StartUpInfo);
    lpTitle:= PChar(ConsoleTitle);
    Help := WinStaName + '\' + DeskTopName;
    lpDesktop:= PChar(Help);
  end;

  // Step 5: create the child process
  Runned :=  CreateProcessAsUser(hUserToken, nil, PChar(CommandLine),
                                 nil, nil, False, CREATE_NEW_CONSOLE or CREATE_NEW_PROCESS_GROUP,
                                 Environment, nil, StartUpInfo, ProcInfo);

  if(not(Runned)) then begin
    case GetLastError of
      ERROR_PRIVILEGE_NOT_HELD:
        {raise EJclCreateProcessError.CreateResRecFmt(@RsCreateProcPrivilegesMissing,
          [GetPrivilegeDisplayName(SE_ASSIGNPRIMARYTOKEN_NAME), SE_ASSIGNPRIMARYTOKEN_NAME,
          GetPrivilegeDisplayName(SE_INCREASE_QUOTA_NAME), SE_INCREASE_QUOTA_NAME]); }
          log('ERROR_PRIVILEGE_NOT_HELD :: ' + IntToStr(GetLastError));
      ERROR_FILE_NOT_FOUND:
        //raise EJclCreateProcessError.CreateResRecFmt(@RsCreateProcCommandNotFound, [CommandLine]);
          log('ERROR_FILE_NOT_FOUND :: ' + IntToStr(GetLastError));
      else
        //raise EJclCreateProcessError.CreateResRec(@RsCreateProcFailed);
          log('ERROR NUM :: ' + IntToStr(GetLastError));
    end;
  end;

  // clean up
  CloseWindowStation(hWindowStation);
  CloseDesktop(hDesktop);
  CloseHandle(hUserToken);

  if Runned then begin
    WaitForSingleObject(ProcInfo.hProcess, INFINITE);
    { Free the Handles }
    CloseHandle(ProcInfo.hProcess);
    CloseHandle(ProcInfo.hThread);
  end;
end;

Actually, how to make it so that the proga.exe launched from under the service works fine?
Already tried a lot, I do not understand where else to look for an error.
There was a suspicion that user1 lacked some rights in the "local security policies", tried to give him a variety of rights, nothing helped.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
O
OvLab, 2014-12-27
@OvLab

Perhaps the error lies in the contents of the CommandLine. For example, a path or file is not available to the SYSTEM user under which the services are running.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question