![]() |
Named Pipes zwischen Service und eingeschränktem Programm
Ich habe ein für mich beunruhigendes Problem.
Nachdem ich jetzt eine funktionierende Named-Pipes-Kommunikation zwischen einem Service (der hier u.a. als Pipes-Server dient) und einem normalen Programm (das sich als Client verbinden kann) implementiert habe, kommt mir der böse Verdacht auf, dass das nur funktioniert, wenn das Programm nicht unter einem eingeschränkten Benutzeraccount läuft. Da das ganze so weit richtig funktioniert hat, poste ich mal nur die entscheidende Stelle im Code:
Delphi-Quellcode:
CallNamedPipe schlägt fehl, wenn das Programm mit eingeschränkten Rechten läuft und es kommt die Meldung 'PIPE antwortet nicht.'. (Wenn der Service/Server nicht aktiv ist, dann kommt die korrekte Meldung, dass die Pipe nicht vorhanden sei. Auch mit eingeschränkten Rechten wird also nocht erkannt, ob die Pipe vorhanden ist oder nicht.)
function TPipeClient.ProcessMsg(aMsg: RPIPEMessage): RPIPEMessage;
begin CalcMsgSize(aMsg); Result.Size := SizeOf(Result); if WaitNamedPipe(PChar(FPipeName), 10) then if not CallNamedPipe( PChar(FPipeName), @aMsg, aMsg.Size, @Result, Result.Size, Result.Size, 3000 ) then MessageDlg('PIPE antwortet nicht.', mtError, [mbOK], 0) else else MessageDlg('PIPE ist nicht vorhanden.', mtError, [mbOK], 0); end; Kann man da irgendwas machen? Irgendwie fühle ich mich hier von Windows auf den Schlips getreten... Es muss doch möglich sein, dass selbst ein eingeschränktes Programm noch über Pipes kommunizieren kann!? |
Re: Named Pipes zwischen Service und eingeschränktem Program
Welcher Error Code wird zurückgegeben (GetLastError)?
|
Re: Named Pipes zwischen Service und eingeschränktem Program
5, Access is denied
|
Re: Named Pipes zwischen Service und eingeschränktem Program
Dann hast du beim Erstellen der Pipe einen zu strengen Security Descriptor angegeben. Windows ermöglicht dir, zu entscheiden, wer mit der Pipe kommunizieren darf und wer nicht.
|
Re: Named Pipes zwischen Service und eingeschränktem Program
Zeig mal bitte deinen Code der die Pipe erstellt.
|
Re: Named Pipes zwischen Service und eingeschränktem Program
Hm, sind die Flags bei CreateNamedPipe jetzt zu streng?
Delphi-Quellcode:
procedure TPipeServer.StartUpServer;
begin try // Pipe bereits vorhanden? if WaitNamedPipe(PChar(FPipeName), 100 {ms}) then raise Exception.Create('PIPE bereits vorhanden.'); // Pipe erstellen FHandle := CreateNamedPipe( PChar(FPipeName), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, SizeOf(RPIPEMessage), SizeOf(RPIPEMessage), NMPWAIT_USE_DEFAULT_WAIT, nil ); // Pipe erstellt? if FHandle = INVALID_HANDLE_VALUE then raise Exception.Create('PIPE konnte nicht erstellt werden.'); except end; end; |
Re: Named Pipes zwischen Service und eingeschränktem Program
Als letzten Parameter solltest du einen Zeiger auf eine gültige SECURITY_ATTRIBUTES-Struktur angeben, in der du den Security Descriptor festlegen kannst.
|
Re: Named Pipes zwischen Service und eingeschränktem Program
Das sollte in diesem Falle nicht wirklich notwendig sein. Eine NULL DACL bedeutet "Zugriff für jeden".
|
Re: Named Pipes zwischen Service und eingeschränktem Program
Ja, aber du musst unterscheiden zwischen DACL nil und Security Descriptor nil. Security Descriptor nil bedeutet Standard-Descriptor, und dieser ist bei Benutzer System glücklicherweise sehr restriktiv.
|
Re: Named Pipes zwischen Service und eingeschränktem Program
Kannst Du mir bitte ein Beispiel geben, wie ich das richtig machen muss?
|
Re: Named Pipes zwischen Service und eingeschränktem Program
Delphi-Quellcode:
Ist ein kleiner Pipe Server, der eine Pipe mit einer DACL die Jedem im System explizit Lese und Schreibrechte erlaubt erstellt.
program pipeserver;
{$APPTYPE CONSOLE} uses jwawinbase, jwawintype, jwawinnt, jwaaccctrl, jwaaclapi, jwawinerror, sysutils; function InstanceThread(PipeHandle : dword) : dword; stdcall; var Value : dword; tmp : dword; begin result := 0; ReadFile(PipeHandle, @Value, 4, @tmp, nil); WriteFile(PipeHandle, @Value, 4, @tmp, nil); FlushFileBuffers(PipeHandle); DisconnectNamedPipe(PipeHandle); CloseHandle(PipeHandle); end; function ServerThread(unused : dword) : dword; stdcall; const BUFSIZE = 4; var PipeHandle : dword; Connected : boolean; SA : LPSECURITY_ATTRIBUTES; ACL : PACL; Group : TRUSTEE; EA : PEXPLICIT_ACCESS; SD : PSECURITY_DESCRIPTOR; ACL_SIZE : ACL_SIZE_INFORMATION; tmp : dword; begin Group.MultipleTrusteeOperation := NO_MULTIPLE_TRUSTEE; Group.pMultipleTrustee := nil; Group.ptstrName := 'Jeder'; Group.TrusteeForm := TRUSTEE_IS_NAME; Group.TrusteeType := TRUSTEE_IS_GROUP; new(EA); EA^.grfAccessMode := GRANT_ACCESS; EA^.grfAccessPermissions := GENERIC_READ or GENERIC_WRITE; EA^.grfInheritance := NO_INHERITANCE; EA^.Trustee := group; SetEntriesInAcl(1, EA, nil, ACL); GetAclInformation(ACL, @ACL_SIZE, sizeof(ACL_SIZE), AclSizeInformation); getmem(SD, SECURITY_DESCRIPTOR_MIN_LENGTH + ACL_SIZE.AclBytesFree + ACL_SIZE.AclBytesInUse); InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(sd, TRUE, ACL, FALSE); new(SA); sa^.bInheritHandle := FALSE; sa^.lpSecurityDescriptor := sd; sa^.nLength := sizeof(sa); while true do begin PipeHandle := CreateNamedPipeW('\\.\pipe\demopipe', PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, INFINITE, SA); if PipeHandle = INVALID_HANDLE_VALUE then begin sleep(100); continue; end; Connected := ConnectNamedPipe(PipeHandle, nil) or (GetLastError = ERROR_PIPE_CONNECTED); if Connected then begin tmp := CreateThread(nil, 0, @InstanceThread, Pointer(PipeHandle), 0, nil); if tmp = 0 then begin DisconnectNamedPipe(PipeHandle); CloseHandle(PipeHandle); continue; end else CloseHandle(tmp); end else CloseHandle(PipeHandle); end; LocalFree(cardinal(ACL)); freemem(SD); dispose(EA); end; var tmp : dword; begin write('Starting ServerThread: '); tmp := CreateThread(nil, 0, @ServerThread, nil, 0, nil); writeln(tmp <> 0); CloseHandle(tmp); readln; end. |
Re: Named Pipes zwischen Service und eingeschränktem Program
Danke!
Sieht recht kompliziert aus. Ich habe parallel hier im Forum noch nach dem Stichwort gesucht und ![]() Tun es die paar Zeilen, um FSA zu bestimmen, auch? Oder gibt es irgendwelche Bedenken?
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var FSA : SECURITY_ATTRIBUTES; FSD : SECURITY_DESCRIPTOR; pch1: shortstring; begin InitializeSecurityDescriptor(@FSD, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(@FSD, True, nil, False); FSA.lpSecurityDescriptor := @FSD; FSA.nLength := sizeof(SECURITY_ATTRIBUTES); FSA.bInheritHandle := True; Pipe:= CreateNamedPipe(PChar('\\.\pipe\<test>'), PIPE_ACCESS_DUPLEX or FILE_FLAG_WRITE_THROUGH, PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_NOWAIT, PIPE_UNLIMITED_INSTANCES, 1024, 1024, 50, @FSA); end; |
Re: Named Pipes zwischen Service und eingeschränktem Program
Funktioniert genauso. Allerdings solltest Du Dir bewusst sein, das alles und jeder dann auf die Pipe zugreifen kann - auch übers Netzwerk und ohne Authentifizierung!
|
Re: Named Pipes zwischen Service und eingeschränktem Program
Hm, also mein Service prüft den Sender der Nachricht auf seine Signatur und agiert nur dann, wenn es sich um meine Signatur handelt.
Dennoch wäre es wahrscheinlich nicht verkehrt, die beiden Punkte Netzwerk und Authentifizierung in den Code einzubeziehen. Ich habe Deinen Code nun genauer angeschaut. Ich verstehe zwar nicht alles davon, aber die Zeile Group.ptstrName := 'Jeder'; macht mich stutzig. Die starre Angabe, würde dann doch nur auf deutschen Betriebssystemen funktionieren, oder? Ich nehme an, dass es nicht einfach einen Flag-Wert gibt, der nur lokal angemeldete Benutzer zulässt? :gruebel: |
Re: Named Pipes zwischen Service und eingeschränktem Program
Mit der TRUSTEE-Struktur kann man Gruppen oder Benutzer nicht nur per Name, sondern auch per SID angeben.
![]() |
Re: Named Pipes zwischen Service und eingeschränktem Program
Nunja, das war Code aus einem kleinen Beispiel von mir, wie man eine Pipe erstellt auf die nur Mitglieder einer bestimmten Gruppe Zugriff haben. Für Deine Zwecke wäre es wahrscheinlich besser den Zugriff an einen Well Known SID zu koppeln. Der Code würde dann so aussehen:
Delphi-Quellcode:
Eine Liste der Well Known SIDs gibts hier: http://msdn.microsoft.com/en-us/library/aa379650(VS.85).aspx
program pipeserver;
{$APPTYPE CONSOLE} uses jwawinbase, jwawintype, jwawinnt, jwaaccctrl, jwaaclapi, jwawinerror, sysutils; function InstanceThread(PipeHandle : dword) : dword; stdcall; var Value : dword; tmp : dword; begin result := 0; ReadFile(PipeHandle, @Value, 4, @tmp, nil); WriteFile(PipeHandle, @Value, 4, @tmp, nil); FlushFileBuffers(PipeHandle); DisconnectNamedPipe(PipeHandle); CloseHandle(PipeHandle); end; function ServerThread(unused : dword) : dword; stdcall; const BUFSIZE = 4; var PipeHandle : dword; Connected : boolean; SA : LPSECURITY_ATTRIBUTES; ACL : PACL; Group : TRUSTEE; EA : PEXPLICIT_ACCESS; SD : PSECURITY_DESCRIPTOR; ACL_SIZE : ACL_SIZE_INFORMATION; WellKnownSID : PSID; tmp : dword; begin tmp := SECURITY_MAX_SID_SIZE; GetMem(WellKnownSID, tmp); CreateWellKnownSid(WinWorldSid, nil, WellKnownSID, tmp); Group.MultipleTrusteeOperation := NO_MULTIPLE_TRUSTEE; Group.pMultipleTrustee := nil; Group.ptstrName := Pointer(WellKnownSID); Group.TrusteeForm := TRUSTEE_IS_SID; Group.TrusteeType := TRUSTEE_IS_WELL_KNOWN_GROUP; new(EA); EA^.grfAccessMode := GRANT_ACCESS; EA^.grfAccessPermissions := GENERIC_READ or GENERIC_WRITE; EA^.grfInheritance := NO_INHERITANCE; EA^.Trustee := group; SetEntriesInAcl(1, EA, nil, ACL); GetAclInformation(ACL, @ACL_SIZE, sizeof(ACL_SIZE), AclSizeInformation); getmem(SD, SECURITY_DESCRIPTOR_MIN_LENGTH + ACL_SIZE.AclBytesFree + ACL_SIZE.AclBytesInUse); InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(sd, TRUE, ACL, FALSE); new(SA); sa^.bInheritHandle := FALSE; sa^.lpSecurityDescriptor := sd; sa^.nLength := sizeof(sa); while true do begin PipeHandle := CreateNamedPipeW('\\.\pipe\demopipe', PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, INFINITE, SA); if PipeHandle = INVALID_HANDLE_VALUE then begin sleep(100); continue; end; Connected := ConnectNamedPipe(PipeHandle, nil) or (GetLastError = ERROR_PIPE_CONNECTED); if Connected then begin tmp := CreateThread(nil, 0, @InstanceThread, Pointer(PipeHandle), 0, nil); if tmp = 0 then begin DisconnectNamedPipe(PipeHandle); CloseHandle(PipeHandle); continue; end else CloseHandle(tmp); end else CloseHandle(PipeHandle); end; LocalFree(cardinal(ACL)); freemem(SD); freemem(WellKnownSID); dispose(EA); end; var tmp : dword; begin write('Starting ServerThread: '); tmp := CreateThread(nil, 0, @ServerThread, nil, 0, nil); writeln(tmp <> 0); CloseHandle(tmp); readln; end. In Deinem Falle wäre wohl WinLocalSid nützlich. Man kann den Code übrigens noch stark vereinfachen indem man die JWSCL benutzt. Allerdings ist mir die JWSCL für meinen Geschmack zu viel Overhead. |
Re: Named Pipes zwischen Service und eingeschränktem Program
Wäre es hier nicht klüger, den Speicher für WellKnownSID auf dem Stack zu reservieren?
|
Re: Named Pipes zwischen Service und eingeschränktem Program
Welchen Vorteil erhoffst Du Dir dadurch?
|
Re: Named Pipes zwischen Service und eingeschränktem Program
Zitat:
Da ich die JWSCL so oder so bereits eingebunden habe, würd ich auch gern den vereinfachten Code nehmen. Zitat:
|
Re: Named Pipes zwischen Service und eingeschränktem Program
@0xF30FC7: Ich finde einfach, dass man den Memory Manager nicht bemühen sollte, wenn der Stack es genauso tut, denn er ist in jedem Fall langsamer und hat einen nicht zu vernachlässigenden Speicher-Overhead.
Das gleiche gilt für EA. Bei SD würde ich allerdings auch den MM nehmen, da du die Größe nicht im Voraus weißt. |
Re: Named Pipes zwischen Service und eingeschränktem Program
Sowas ?
![]()
Delphi-Quellcode:
program Server;
{$APPTYPE CONSOLE} uses SysUtils, Dialogs, jwaWindows, jwsclDescriptor, jwsclACL, JwsclSID, jwsclKnownSid, jwsclMapping, jwsclToken, jwsclStrings; type TMessage = record Data : array[0..1023] of Char; end; var PipeHandle : THandle; pSecAttr : TSecurityAttributes; SD : TJwSecurityDescriptor; i : Integer; lpData : TMessage; iWritten, iRead : DWORD; PipeToken : TJwSecurityToken; c : Char; begin Writeln('Server Pipe'); JwInitWellKnownSIDs; //for the JwXxxxSID variables SD := TJwSecurityDescriptor.Create; SD.DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create(nil,[],GENERIC_ALL,JwAdministratorsSID, false)); SD.DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create(nil,[],GENERIC_ALL,JwLocalSystemSID, false)); Writeln('Do you want to allow the actual logged on user to connect to the pipe? (y/n)'); Writeln('If you say yes, your user will succed to connect to the pipe. However if you are using Vista, the user will get'+ 'the string ''denied'' from the server because he is deny only for admins. Is the process started elevated or on XP it should work fine.'); Writeln('If you say no, the user will only allowed to be connected to the pipe, if he is an administrator or system '); Write('[Y/N]:'); Read(c); if UpCase(c) = 'Y' then SD.DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create(nil,[],GENERIC_ALL,JwLocalGroupSID, false)); //ShowMessage(SD.GetTextMap(TJwSecurityPipeMapping)); //remove Everyone group if available i := SD.DACL.FindSID(JwWorldSID); if i > -1 then begin {If lpSecurityAttributes is NULL, the named pipe gets a default security descriptor and the handle cannot be inherited. The ACLs in the default security descriptor for a named pipe grant full control to the LocalSystem account, administrators, and the creator owner. They also grant read access to members of the Everyone group and the anonymous account. } SD.DACL.Delete(i); //remove Everyone group end; pSecAttr := SD.Create_SAEx(true); try PipeHandle := CreateNamedPipe( '\\.\pipe\JwsclPipeTest',//__in LPCTSTR lpName, PIPE_ACCESS_DUPLEX or FILE_FLAG_FIRST_PIPE_INSTANCE,//__in DWORD dwOpenMode, PIPE_TYPE_MESSAGE or // message type pipe PIPE_WAIT,//__in DWORD dwPipeMode, 1,//__in DWORD nMaxInstances, 4096,//__in DWORD nOutBufferSize, 4096,//__in DWORD nInBufferSize, 0,//__in DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES(@pSecAttr)//__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes ); if PipeHandle = INVALID_HANDLE_VALUE then RaiseLastOSError; finally SD.Free_SAEx(pSecAttr); FreeAndNil(SD); end; try if not ConnectNamedPipe(PipeHandle,nil) then RaiseLastOSError; if not ReadFile(PipeHandle,@lpData,sizeof(lpData),@iRead, nil) then RaiseLastOSError; writeln('Data from other side: ',lpData.Data); try //throws exception if fails TJwSecurityToken.ImpersonateNamedPipeClient(PipeHandle); //we are in context of user now try if JwCheckAdministratorAccess then begin lpData.Data := 'Success'#0#0; Writeln('Client is approved.'); end else begin lpData.Data := 'Denied.'#0#0; Writeln('Client is denied.'); end; WriteFile(PipeHandle, @lpData,sizeof(lpData), @iWritten, nil); finally TJwSecurityToken.RevertToSelf; end; finally end; finally Sleep(2000); //or wait for more connections as shown above CloseHandle(PipeHandle); end; writeln('Hit enter...'); readln; end. |
Re: Named Pipes zwischen Service und eingeschränktem Program
Zitat:
@CodeX: Dezipaitor hat ja bereits den Beispiel Quelltext aus der JWSCL gepostet. Mein Beispiel würde so aussehen:
Delphi-Quellcode:
Das PipeServer Executable ist übrigens dadurch von 58kb auf 610kb angewachsen ;).
program pipeserver;
{$APPTYPE CONSOLE} uses SysUtils, jwaWindows, jwsclDescriptor, jwsclACL, JwsclSID, jwsclKnownSid; function InstanceThread(PipeHandle : dword) : dword; stdcall; var Value : dword; tmp : dword; begin result := 0; ReadFile(PipeHandle, @Value, 4, @tmp, nil); WriteFile(PipeHandle, @Value, 4, @tmp, nil); FlushFileBuffers(PipeHandle); DisconnectNamedPipe(PipeHandle); CloseHandle(PipeHandle); end; function ServerThread(unused : dword) : dword; stdcall; const BUFSIZE = 4; var PipeHandle : dword; Connected : boolean; tmp : dword; SD : TJwSecurityDescriptor; SA : TSecurityAttributes; begin JwInitWellKnownSIDs; SD := TJwSecurityDescriptor.Create; SD.DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create(nil, [], GENERIC_ALL, JwLocalGroupSID, false)); SA := SD.Create_SAEx(true, false); while true do begin PipeHandle := CreateNamedPipeW('\\.\pipe\demopipe', PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFSIZE, BUFSIZE, INFINITE, @SA); if PipeHandle = INVALID_HANDLE_VALUE then begin sleep(100); continue; end; Connected := ConnectNamedPipe(PipeHandle, nil) or (GetLastError = ERROR_PIPE_CONNECTED); if Connected then begin tmp := CreateThread(nil, 0, @InstanceThread, Pointer(PipeHandle), 0, nil); if tmp = 0 then begin DisconnectNamedPipe(PipeHandle); CloseHandle(PipeHandle); continue; end else CloseHandle(tmp); end else CloseHandle(PipeHandle); end; SD.Free_SAEx(SA); SD.Free; end; var tmp : dword; begin write('Starting ServerThread: '); tmp := CreateThread(nil, 0, @ServerThread, nil, 0, nil); writeln(tmp <> 0); CloseHandle(tmp); readln; end. |
Re: Named Pipes zwischen Service und eingeschränktem Program
Ich arbeite dran die Units bisschen kleiner zu machen. Besonders da JwaWindows und SysUtils drin sind, ist das alles etwas groß.
Dummerweise gibt es nur wenig Zeit und wenig Hilfe für die Aufgaben :wall: |
Re: Named Pipes zwischen Service und eingeschränktem Program
Ich habe auch grad erlebt,dass es garnicht möglich ist SysUtils zu ersetzen.
Zudem, wenn man die einzelnen Units JwaXXX der JwaWindows vorzieht, dann muss man damit leben, dass je nach Reihenfolge dieser Units in der Uses Klausel, man verschiedene Typen verwendet. Diese Typen können nämlich doppelt in verschiedenen JwaUnits definiert worden sein. Dann muss man explizit casten. |
Re: Named Pipes zwischen Service und eingeschränktem Program
Ich denke, mein Problem ist so weit gelöst. Danke schön Euch allen!! :cheers:
|
Re: Named Pipes zwischen Service und eingeschränktem Program
Ich verfolge ja das ganze Thema und bin selber am Schreiben eines Dienstes, der eine NamedPipe zur Verfügung stellt und für eine definierte Benutzergruppe diese zur Verfügung stellen soll.
Jedoch möchte ich keine JEDI-Komponenten verwenden .... Probleme bekomme ich bei dem Code u.a. bei [Fehler] uService.pas(89): Undefinierter Bezeichner: 'LPSECURITY_ATTRIBUTES' [Fehler] uService.pas(90): Undefinierter Bezeichner: 'PSECURITY_ATTRIBUTES' [Fehler] uService.pas(93): Undefinierter Bezeichner: 'ACL_SIZE_INFORMATION' [Fehler] uService.pas(98): Undefinierter Bezeichner: 'MultipleTrusteeOperations' [Fehler] uService.pas(100): Undefinierter Bezeichner: 'pstrName' [...] [Fehler] uService.pas(111): Inkompatible Typen: '_ACL' und 'PACL' Derzeitiges uses uses Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs, accctrl, aclapi; |
Re: Named Pipes zwischen Service und eingeschränktem Program
Die musst du alle selbst übersetzen aus C nach Delphi.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:40 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