windows-nt/Source/XPSP1/NT/printscan/print/spooler/spoolss/server/splrpc.c

600 lines
17 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1990-1993 Microsoft Corporation
Module Name:
splrpc.c
Abstract:
This file contains routines for starting and stopping RPC servers.
SpoolerStartRpcServer
SpoolerStopRpcServer
Author:
Krishna Ganugapati krishnaG
Environment:
User Mode - Win32
Revision History:
14-Oct-1993 KrishnaG
Created
25-May-1999 khaleds
Added:
CreateNamedPipeSecurityDescriptor
BuildNamedPipeProtection
--*/
//
// INCLUDES
//
#define NOMINMAX
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <winspool.h>
#include <winsplp.h>
#include <rpc.h>
#include "splsvr.h"
#include "splr.h"
#include "server.h"
#ifndef _SRVRMEM_H_
#include "srvrmem.h"
#endif
WCHAR szSerializeRpc []= L"SerializeRpc";
WCHAR szCallExitProcessOnShutdown []= L"CallExitProcessOnShutdown";
WCHAR szMaxRpcSize []= L"MaxRpcSize";
WCHAR szPrintKey[] = L"System\\CurrentControlSet\\Control\\Print";
#define DEFAULT_MAX_RPC_SIZE 1024*1024 // default maximum rpc data block size in bytes
DWORD dwCallExitProcessOnShutdown = TRUE;
RPC_STATUS
SpoolerStartRpcServer(
VOID)
/*++
Routine Description:
Arguments:
Return Value:
NERR_Success, or any RPC error codes that can be returned from
RpcServerUnregisterIf.
--*/
{
unsigned char * InterfaceAddress;
RPC_STATUS status;
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
BOOL Bool;
HKEY hKey;
DWORD cbData;
DWORD dwSerializeRpc = 0;
DWORD dwType;
DWORD dwMaxRpcSize;
InterfaceAddress = "\\pipe\\spoolss";
// Croft up a security descriptor that will grant everyone
// all access to the object (basically, no security)
//
// We do this by putting in a NULL Dacl.
//
// NOTE: rpc should copy the security descriptor,
// Since it currently doesn't, simply allocate it for now and
// leave it around forever.
//
SecurityDescriptor = CreateNamedPipeSecurityDescriptor();
if (SecurityDescriptor == 0) {
DBGMSG(DBG_ERROR, ("Spoolss: out of memory\n"));
return FALSE;
}
//
// For now, ignore the second argument.
//
status = RpcServerUseProtseqEpA("ncacn_np", 10, InterfaceAddress, SecurityDescriptor);
if (status) {
DBGMSG(DBG_WARN, ("RpcServerUseProtseqEpA 1 = %u\n",status));
return FALSE;
}
//
// For now, ignore the second argument.
//
status = RpcServerUseProtseqEpA("ncalrpc", 10, "spoolss", SecurityDescriptor);
if (status) {
DBGMSG(DBG_WARN, ("RpcServerUseProtseqEpA 2 = %u\n",status));
return FALSE;
}
if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szPrintKey,
0,
KEY_READ,
&hKey)) {
//
// Ignore failure case since we can use the default
//
cbData = sizeof(dwSerializeRpc);
RegQueryValueEx(hKey,
szSerializeRpc,
NULL,
&dwType,
(LPBYTE)&dwSerializeRpc,
&cbData);
//
// This value can be used to control if spooler controls ExitProcess
// on shutdown
//
cbData = sizeof(dwCallExitProcessOnShutdown);
RegQueryValueEx(hKey,
szCallExitProcessOnShutdown,
NULL,
&dwType,
(LPBYTE)&dwCallExitProcessOnShutdown,
&cbData);
//
// dwMaxRpcSize specifies the maximum size in bytes of incoming RPC data blocks.
//
cbData = sizeof(dwMaxRpcSize);
if (RegQueryValueEx(hKey,
szMaxRpcSize,
NULL,
&dwType,
(LPBYTE)&dwMaxRpcSize,
&cbData) != ERROR_SUCCESS) {
dwMaxRpcSize = DEFAULT_MAX_RPC_SIZE;
}
RegCloseKey(hKey);
}
//
// Now we need to add the interface. We can just use the winspool_ServerIfHandle
// specified by the MIDL compiler in the stubs (winspl_s.c).
//
status = RpcServerRegisterIf2( winspool_ServerIfHandle,
0,
0,
0,
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
dwMaxRpcSize,
NULL
);
if (status) {
DBGMSG(DBG_WARN, ("RpcServerRegisterIf = %u\n",status));
return FALSE;
}
if (dwSerializeRpc) {
// By default, rpc will serialize access to context handles. Since
// the spooler needs to be able to have two threads access a context
// handle at once, and it knows what it is doing, we will tell rpc
// not to serialize access to context handles.
I_RpcSsDontSerializeContext();
}
status = RpcMgmtSetServerStackSize(INITIAL_STACK_COMMIT);
if (status != RPC_S_OK) {
DBGMSG(DBG_ERROR, ("Spoolss : RpcMgmtSetServerStackSize = %d\n", status));
}
// The first argument specifies the minimum number of threads to
// create to handle calls; the second argument specifies the maximum
// concurrent calls to handle. The third argument indicates that
// the routine should not wait.
status = RpcServerListen(1,SPL_MAX_RPC_CALLS,1);
if ( status != RPC_S_OK ) {
DBGMSG(DBG_ERROR, ("Spoolss : RpcServerListen = %d\n", status));
}
return (status);
}
#define MAX_ACE 7
#define DBGCHK( Condition, ErrorInfo ) \
if( Condition ) DBGMSG( DBG_WARNING, ErrorInfo )
/*++
Routine Description:
This routine adds prepares the required masks and flags required for the
DACL on the named pipes used by RPC
Arguments:
None
Return Value:
An allocated Security Descriptor
--*/
PSECURITY_DESCRIPTOR
CreateNamedPipeSecurityDescriptor(
VOID
)
{
UCHAR AceType[MAX_ACE];
PSID AceSid[MAX_ACE];
BYTE InheritFlags[MAX_ACE];
DWORD AceCount;
PSECURITY_DESCRIPTOR ServerSD = NULL;
//
// For Code optimization we replace 5 individaul
// SID_IDENTIFIER_AUTHORITY with an array of
// SID_IDENTIFIER_AUTHORITYs
// where
// SidAuthority[0] = UserSidAuthority
// SidAuthority[1] = PowerSidAuthority
// SidAuthority[2] = EveryOneSidAuthority
// SidAuthority[3] = CreatorSidAuthority
// SidAuthority[4] = SystemSidAuthority
// SidAuthority[5] = AdminSidAuthority
//
SID_IDENTIFIER_AUTHORITY SidAuthority[MAX_ACE] = {
SECURITY_NT_AUTHORITY,
SECURITY_NT_AUTHORITY,
SECURITY_WORLD_SID_AUTHORITY,
SECURITY_NT_AUTHORITY,
SECURITY_CREATOR_SID_AUTHORITY,
SECURITY_NT_AUTHORITY,
SECURITY_NT_AUTHORITY
};
//
// For code optimization we replace 6 individual Sids with
// an array of Sids; On Whistler, Anonymous user will no longer
// be granted access to resources whose ACLs grant access to Everyone.
//
// We need to give access to Anonymous user to access the RPC pipe
// for the reverse RPC connection when RPC calls could come as
// Anonymous from either NT4 machines, Whistler Personal or Whitler
// across domains.
//
// Sid[0] = UserSid
// Sid[1] = PowerSid
// Sid[2] = EveryOne
// Sid[3] = Anonymous
// Sid[4] = CreatorSid
// Sid[5] = SystemSid
// Sid[6] = AdminSid
//
PSID Sids[MAX_ACE] = {NULL,NULL,NULL,NULL,NULL};
ACCESS_MASK AceMask[MAX_ACE] = {
FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE ,
FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE ,
(FILE_GENERIC_READ | FILE_WRITE_DATA | FILE_ALL_ACCESS) &
~WRITE_DAC &~WRITE_OWNER & ~DELETE & ~FILE_CREATE_PIPE_INSTANCE,
(FILE_GENERIC_READ | FILE_WRITE_DATA | FILE_ALL_ACCESS) &
~WRITE_DAC &~WRITE_OWNER & ~DELETE & ~FILE_CREATE_PIPE_INSTANCE,
STANDARD_RIGHTS_ALL | FILE_GENERIC_WRITE | FILE_GENERIC_READ | FILE_ALL_ACCESS,
STANDARD_RIGHTS_ALL | FILE_GENERIC_WRITE | FILE_GENERIC_READ | FILE_ALL_ACCESS,
STANDARD_RIGHTS_ALL | FILE_GENERIC_WRITE | FILE_GENERIC_READ | FILE_ALL_ACCESS
};
DWORD SubAuthorities[3*MAX_ACE] = {
2 , SECURITY_BUILTIN_DOMAIN_RID , DOMAIN_ALIAS_RID_USERS ,
2 , SECURITY_BUILTIN_DOMAIN_RID , DOMAIN_ALIAS_RID_POWER_USERS ,
1 , SECURITY_WORLD_RID , 0 ,
1 , SECURITY_ANONYMOUS_LOGON_RID , 0 ,
1 , SECURITY_CREATOR_OWNER_RID , 0 ,
1 , SECURITY_LOCAL_SYSTEM_RID , 0 ,
2 , SECURITY_BUILTIN_DOMAIN_RID , DOMAIN_ALIAS_RID_ADMINS
};
//
// Name Pipe SD
//
for(AceCount = 0;
( (AceCount < MAX_ACE) &&
AllocateAndInitializeSid(&SidAuthority[AceCount],
(BYTE)SubAuthorities[AceCount*3],
SubAuthorities[AceCount*3+1],
SubAuthorities[AceCount*3+2],
0, 0, 0, 0, 0, 0,
&Sids[AceCount]));
AceCount++)
{
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
AceSid[AceCount] = Sids[AceCount];
InheritFlags[AceCount] = 0;
}
if(AceCount == MAX_ACE)
{
if(!BuildNamedPipeProtection( AceType,
AceCount,
AceSid,
AceMask,
InheritFlags,
NULL,
NULL,
NULL,
&ServerSD ) )
{
DBGMSG( DBG_WARNING,( "Couldn't buidl Named Pipe protection" ) );
}
}
else
{
DBGMSG( DBG_WARNING,( "Couldn't Allocate and initialize SIDs" ) );
}
for(AceCount=0;AceCount<MAX_ACE;AceCount++)
{
if(Sids[AceCount])
FreeSid( Sids[AceCount] );
}
return ServerSD;
}
BOOL
BuildNamedPipeProtection(
IN PUCHAR AceType,
IN DWORD AceCount,
IN PSID *AceSid,
IN ACCESS_MASK *AceMask,
IN BYTE *InheritFlags,
IN PSID OwnerSid,
IN PSID GroupSid,
IN PGENERIC_MAPPING GenericMap,
OUT PSECURITY_DESCRIPTOR *ppSecurityDescriptor
)
/*++
Routine Description:
This routine builds a self-relative security descriptor ready
to be applied to one of the print manager objects.
If so indicated, a pointer to the last RID of the SID in the last
ACE of the DACL is returned and a flag set indicating that the RID
must be replaced before the security descriptor is applied to an object.
This is to support USER object protection, which must grant some
access to the user represented by the object.
The SACL of each of these objects will be set to:
Audit
Success | Fail
WORLD
(Write | Delete | WriteDacl | AccessSystemSecurity)
Arguments:
AceType - Array of AceTypes.
Must be ACCESS_ALLOWED_ACE_TYPE or ACCESS_DENIED_ACE_TYPE.
AceCount - The number of ACEs to be included in the DACL.
AceSid - Points to an array of SIDs to be granted access by the DACL.
If the target SAM object is a User object, then the last entry
in this array is expected to be the SID of an account within the
domain with the last RID not yet set. The RID will be set during
actual account creation.
AceMask - Points to an array of accesses to be granted by the DACL.
The n'th entry of this array corresponds to the n'th entry of
the AceSid array. These masks should not include any generic
access types.
InheritFlags - Pointer to an array of inherit flags.
The n'th entry of this array corresponds to the n'th entry of
the AceSid array.
OwnerSid - The SID of the owner to be assigned to the descriptor.
GroupSid - The SID of the group to be assigned to the descriptor.
GenericMap - Points to a generic mapping for the target object type.
ppSecurityDescriptor - Receives a pointer to the security descriptor.
This will be allcated from the process' heap, not the spooler's,
and should therefore be freed with LocalFree().
IN DWORD AceCount,
IN PSID *AceSid,
IN ACCESS_MASK *AceMask,
IN BYTE *InheritFlags,
IN PSID OwnerSid,
IN PSID GroupSid,
IN PGENERIC_MAPPING GenericMap,
OUT PSECURITY_DESCRIPTOR *ppSecurityDescriptor
Return Value:
TBS.
--*/
{
SECURITY_DESCRIPTOR Absolute;
PSECURITY_DESCRIPTOR Relative = NULL;
PACL TmpAcl= NULL;
PACCESS_ALLOWED_ACE TmpAce;
DWORD SDLength;
DWORD DaclLength;
DWORD i;
BOOL OK;
//
// The approach is to set up an absolute security descriptor that
// looks like what we want and then copy it to make a self-relative
// security descriptor.
//
OK = InitializeSecurityDescriptor( &Absolute,
SECURITY_DESCRIPTOR_REVISION1 );
DBGCHK( !OK, ( "Failed to initialize security descriptor. Error %d", GetLastError() ) );
//
// Owner
//
OK = SetSecurityDescriptorOwner( &Absolute, OwnerSid, FALSE );
DBGCHK( !OK, ( "Failed to set security descriptor owner. Error %d", GetLastError() ) );
//
// Group
//
OK = SetSecurityDescriptorGroup( &Absolute, GroupSid, FALSE );
DBGCHK( !OK, ( "Failed to set security descriptor group. Error %d", GetLastError() ) );
//
// Discretionary ACL
//
// Calculate its length,
// Allocate it,
// Initialize it,
// Add each ACE
// Set ACE as InheritOnly if necessary
// Add it to the security descriptor
//
DaclLength = (DWORD)sizeof(ACL);
for (i=0; i<AceCount; i++) {
DaclLength += GetLengthSid( AceSid[i] ) +
(DWORD)sizeof(ACCESS_ALLOWED_ACE) -
(DWORD)sizeof(DWORD); //Subtract out SidStart field length
}
TmpAcl = SrvrAllocSplMem( DaclLength );
if (!TmpAcl) {
DBGCHK( !TmpAcl, ( "Out of heap space: Can't allocate ACL." ) );
*ppSecurityDescriptor = NULL;
return(FALSE);
}
OK = InitializeAcl( TmpAcl, DaclLength, ACL_REVISION2 );
DBGCHK( !OK, ( "Failed to set initialize ACL. Error %d", GetLastError() ) );
for (i=0; i<AceCount; i++)
{
if( AceType[i] == ACCESS_ALLOWED_ACE_TYPE )
OK = AddAccessAllowedAce ( TmpAcl, ACL_REVISION2, AceMask[i], AceSid[i] );
else
OK = AddAccessDeniedAce ( TmpAcl, ACL_REVISION2, AceMask[i], AceSid[i] );
DBGCHK( !OK, ( "Failed to add access-allowed ACE. Error %d", GetLastError() ) );
if (InheritFlags[i] != 0)
{
OK = GetAce( TmpAcl, i, (LPVOID *)&TmpAce );
DBGCHK( !OK, ( "Failed to get ACE. Error %d", GetLastError() ) );
TmpAce->Header.AceFlags = InheritFlags[i];
}
}
OK = SetSecurityDescriptorDacl (&Absolute, TRUE, TmpAcl, FALSE );
DBGCHK( !OK, ( "Failed to set security descriptor DACL. Error %d", GetLastError() ) );
//
// Convert the Security Descriptor to Self-Relative
//
// Get the length needed
// Allocate that much memory
// Copy it
// Free the generated absolute ACLs
//
SDLength = GetSecurityDescriptorLength( &Absolute );
Relative = LocalAlloc( 0, SDLength );
if (!Relative) {
DBGCHK( !Relative, ( "Out of heap space: Can't allocate security descriptor" ) );
goto Fail;
}
OK = MakeSelfRelativeSD(&Absolute, Relative, &SDLength );
if (!OK) {
DBGCHK( !OK, ( "Failed to create self-relative security descriptor DACL. Error %d", GetLastError() ) );
goto Fail;
}
SrvrFreeSplMem( TmpAcl );
*ppSecurityDescriptor = Relative;
return( OK );
Fail:
if (TmpAcl){
FreeSplMem(TmpAcl);
}
if (Relative) {
LocalFree(Relative);
}
*ppSecurityDescriptor = NULL;
return(FALSE);
}