By default, when you create a named pipe under Windows, it is accessible only by other applications running under the same user account. Under XP, it worked if the two users were part of the same group, or at least did share some security policy attributes. But, for security reasons, if the background service runs as local system while the application with the user interface runs as the logged on user, you'll receive a ERROR_ACCESS_DENIED flag under Vista and Seven.
If you are willing to open up access to your pipe to all clients, you'd have to explicitly create a TSecurityAttributes record instance, initialized with full user rights:
procedure InitializeSecurity(var SA: TSecurityAttributes; var SD); begin fillchar(SD,SECURITY_DESCRIPTOR_MIN_LENGTH,0); // Initialize the new security descriptor if InitializeSecurityDescriptor(@SD, SECURITY_DESCRIPTOR_REVISION) then begin // Add a NULL descriptor ACL to the security descriptor if SetSecurityDescriptorDacl(@SD, true, nil, false) then begin // Set up the security attributes structure SA.nLength := sizeof(TSecurityAttributes); SA.lpSecurityDescriptor := @SD; SA.bInheritHandle := true; exit; // mark OK end; end; fillchar(SA,sizeof(SA),0); // mark error: no security end;
This works fine, but it doesn't allow you remote access to a service from another computer.
I've tried to implement what Microsoft told officially on http://support.microsoft.com/kb/813414, but I didn't succeed as expected. The trick is to allow access as anonymous user to the pipe...
Here is the code I wrote from original c sample
function GetUserSid(var SID: PSID; var Token: THandle): boolean; var TokenUserSize: DWORD; TokenUserP: PSIDAndAttributes; begin result := false; if not OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, Token) then if (GetLastError <> ERROR_NO_TOKEN) or not OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, Token) then Exit; TokenUserP := nil; TokenUserSize := 0; try if not GetTokenInformation(Token, TokenUser, nil, 0, TokenUserSize) and (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then Exit; TokenUserP := AllocMem(TokenUserSize); if not GetTokenInformation(Token, TokenUser, TokenUserP, TokenUserSize, TokenUserSize) then Exit; SID := TokenUserP^.Sid; result := true; finally FreeMem(TokenUserP); end; end; {$ALIGN ON} type ACE_HEADER = record AceType: BYTE; AceFlags: BYTE; AceSize: WORD; end; ACCESS_ALLOWED_ACE = record Header: ACE_HEADER; Mask: ACCESS_MASK; SidStart: DWORD; end; {$A8} procedure InitializeSecurity(var SA: TSecurityAttributes; var SD); const SECURITY_NT_AUTHORITY: TSIDIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5)); SECURITY_ANONYMOUS_LOGON_RID = ($00000007); ACL_REVISION = 2; var pSidAnonymous, pSidOwner: PSID; dwAclSize: integer; ACLP: PACL; Token: THandle; begin fillchar(SD,SECURITY_DESCRIPTOR_MIN_LENGTH,0); // Initialize the new security descriptor if InitializeSecurityDescriptor(@SD, SECURITY_DESCRIPTOR_REVISION) and GetUserSid(pSidOwner,Token) then begin AllocateAndInitializeSid(SECURITY_NT_AUTHORITY,1, SECURITY_ANONYMOUS_LOGON_RID,0,0,0,0,0,0,0,pSidAnonymous); try dwAclSize := sizeof(TACL) + 2 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) + GetLengthSid(pSidAnonymous) + GetLengthSid(pSidOwner) ; ACLP := AllocMem(dwAclSize); try InitializeAcl(ACLP^,dwAclSize,ACL_REVISION); if not AddAccessAllowedAce(ACLP^,ACL_REVISION, GENERIC_ALL,pSidOwner) then exit; if not AddAccessAllowedAce(ACLP^,ACL_REVISION, GENERIC_READ or GENERIC_WRITE,pSidAnonymous) then exit; if SetSecurityDescriptorDacl(@SD,true,ACLP,false) then begin // Set up the security attributes structure SA.nLength := sizeof(TSecurityAttributes); SA.lpSecurityDescriptor := @SD; SA.bInheritHandle := true; exit; // mark OK end; finally FreeMem(ACLP); end; finally FreeSid(pSidAnonymous); CloseHandle(Token); end; end; fillchar(SA,sizeof(SA),0); // mark error: no security end;
But it didn't work as expected. Of course, I added the name of the pipe to the SYSTEM\CurrentControlSet\Services\lanmanserver\parameters\NullSessionPipes registry entry, but it didn't work...
Whole source code is available from our source code repository, file SQLite3\SQlite3Commons.pas
You can continue to discuss on our forum, and share any solution...
Of course, we have implemented HTTP/1.1 connection, which works very well remotely over a network, so if you need networking, consider using HTTP... but it could be an interesting challenge to make named pipes work over the network!