Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

533 lines
13 KiB

/*++
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
--*/
#include "precomp.h"
#include "server.h"
#include "srvrmem.h"
#include "splsvr.h"
WCHAR szCallExitProcessOnShutdown []= L"CallExitProcessOnShutdown";
WCHAR szMaxRpcSize []= L"MaxRpcSize";
WCHAR szPrintKey[] = L"System\\CurrentControlSet\\Control\\Print";
CRITICAL_SECTION RpcNamedPipeCriticalSection;
//
// Default RPC buffer max size 50 MB
//
#define DEFAULT_MAX_RPC_SIZE 50 * 1024 * 1024
DWORD dwCallExitProcessOnShutdown = TRUE;
struct
{
BOOL bRpcEndPointEnabled;
ERemoteRPCEndPointPolicy ePolicyValue;
RPC_STATUS RpcStatus;
} gNamedPipeState = {FALSE, RpcEndPointPolicyUnconfigured, RPC_S_OK};
PSECURITY_DESCRIPTOR gpSecurityDescriptor = NULL;
/*++
Routine Description:
Determines the OS suite of the current system.
Arguments:
pSuiteMask - pointer to word that is going to hold
the OS suite.
Return Value:
S_OK if succeeded
--*/
HRESULT
GetOSSuite(
WORD* pSuiteMask
)
{
HRESULT hr = S_OK;
if (!pSuiteMask)
{
hr = HResultFromWin32(ERROR_INVALID_PARAMETER);
}
if (SUCCEEDED(hr))
{
OSVERSIONINFOEX OSVersionInfoEx = {0};
OSVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
*pSuiteMask = 0;
if (GetVersionEx((OSVERSIONINFO*)&OSVersionInfoEx))
{
*pSuiteMask |= OSVersionInfoEx.wSuiteMask;
}
else
{
hr = HResultFromWin32(GetLastError());
}
}
return hr;
}
RPC_STATUS
SpoolerStartRpcServer(
VOID)
/*++
Routine Description:
Arguments:
Return Value:
NERR_Success, or any RPC error codes that can be returned from
RpcServerUnregisterIf.
--*/
{
RPC_STATUS status;
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
BOOL Bool;
HKEY hKey;
DWORD cbData;
DWORD dwType;
DWORD dwMaxRpcSize = DEFAULT_MAX_RPC_SIZE;
WORD OSSuite;
//
// Craft 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.
//
gpSecurityDescriptor = CreateNamedPipeSecurityDescriptor();
if (gpSecurityDescriptor == 0) {
DBGMSG(DBG_ERROR, ("Spoolss: out of memory\n"));
return FALSE;
}
if (FAILED(GetOSSuite(&OSSuite)))
{
DBGMSG(DBG_ERROR, ("Failed to get the OS suite.\n"));
return FALSE;
}
if (OSSuite & (VER_SUITE_BLADE | VER_SUITE_EMBEDDED_RESTRICTED))
{
gNamedPipeState.ePolicyValue = RpcEndPointPolicyDisabled;
}
else
{
gNamedPipeState.ePolicyValue = GetSpoolerNumericPolicyValidate(szRegisterSpoolerRemoteRpcEndPoint,
RpcEndPointPolicyUnconfigured,
RpcEndPointPolicyDisabled);
}
if (gNamedPipeState.ePolicyValue == RpcEndPointPolicyEnabled)
{
if (FAILED(RegisterNamedPipe()))
{
return FALSE;
}
}
else if (gNamedPipeState.ePolicyValue == RpcEndPointPolicyUnconfigured)
{
if (!InitializeCriticalSectionAndSpinCount(&RpcNamedPipeCriticalSection, 0x80000000))
{
return FALSE;
}
}
//
// For now, ignore the second argument.
//
status = RpcServerUseProtseqEpA("ncalrpc", 10, "spoolss", gpSecurityDescriptor);
if (status) {
DBGMSG(DBG_WARN, ("RpcServerUseProtseqEpA 2 = %u\n",status));
return FALSE;
}
if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szPrintKey,
0,
KEY_READ,
&hKey)) {
//
// 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;
}
status = RpcMgmtSetServerStackSize(INITIAL_STACK_COMMIT);
if (status != RPC_S_OK) {
DBGMSG(DBG_ERROR, ("Spoolss : RpcMgmtSetServerStackSize = %d\n", status));
}
if( (status = RpcServerRegisterAuthInfo(0,
RPC_C_AUTHN_WINNT,
0,
0 )) == RPC_S_OK )
{
// 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);
}
/*++
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
--*/
/*++
Name:
CreateNamedPipeSecurityDescriptor
Description:
Creates the security descriptor for the named pipe used by RPC
Arguments:
None.
Return Value:
valid pointer to SECURITY_DESCRIPTOR structure if successful
NULL, on error, use GetLastError
--*/
PSECURITY_DESCRIPTOR
CreateNamedPipeSecurityDescriptor(
VOID
)
{
PSECURITY_DESCRIPTOR pServerSD = NULL;
PCWSTR pszStringSecDesc = L"D:(A;;0x100003;;;BU)"
L"(A;;0x100003;;;PU)"
L"(A;;0x1201fb;;;WD)"
L"(A;;0x1201fb;;;AN)"
L"(A;;FA;;;CO)"
L"(A;;FA;;;SY)"
L"(A;;FA;;;BA)";
//
// Builtin Users - FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE
// Power Users - FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE
// Everyone - FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_EA
// Anonymous - FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE | READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_EA
// Creator Owner - file all access
// System - file all access
// Administrators - file all access
//
// Anonymous has more permission than BU and PU. The extra permission is needed by the back channel (pipe) used by the
// print server to communicate to the client
//
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(pszStringSecDesc,
SDDL_REVISION_1,
&pServerSD,
NULL))
{
pServerSD = NULL;
}
return pServerSD;
}
/*++
Routine Name
ServerAllowRemoteCalls
Routine Description:
Enables the RPC pipe if policy permits.
If the policy is disabled, then it will fail the call.
If the policy is enbled, then it will succeeded the call without doing anything.
If the policy is unconfigured, it will attempt to enable the pipe
if disabled. It keep a retry count and fails directly after 5 times (hardcoded).
Arguments:
None
Return Value:
HRESULT
--*/
HRESULT
ServerAllowRemoteCalls(
VOID
)
{
HRESULT hr = S_OK;
if (gNamedPipeState.ePolicyValue == RpcEndPointPolicyUnconfigured)
{
EnterCriticalSection(&RpcNamedPipeCriticalSection);
//
// Allow retries. Keep RpcStatus for debugging purposes.
//
if (!gNamedPipeState.bRpcEndPointEnabled)
{
hr = RegisterNamedPipe();
gNamedPipeState.bRpcEndPointEnabled = SUCCEEDED(hr);
gNamedPipeState.RpcStatus = StatusFromHResult(hr);
}
LeaveCriticalSection(&RpcNamedPipeCriticalSection);
}
else if (gNamedPipeState.ePolicyValue == RpcEndPointPolicyDisabled)
{
hr = HResultFromWin32(ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED);
DBGMSG(DBG_WARN, ("Remote connections are not allowed.\n"));
}
return hr;
}
/*++
Routine Name
RegisterNamedPipe
Routine Description:
Registers the named pipe protocol.
Arguments:
None
Return Value:
An HRESULT
--*/
HRESULT
RegisterNamedPipe(
VOID
)
{
RPC_STATUS RpcStatus;
HRESULT hr = S_OK;
HANDLE hToken;
if (hToken = RevertToPrinterSelf())
{
RpcStatus = RpcServerUseProtseqEpA("ncacn_np", 10, "\\pipe\\spoolss", gpSecurityDescriptor);
hr = (RpcStatus == RPC_S_OK) ?
S_OK :
MAKE_HRESULT(SEVERITY_ERROR, FACILITY_RPC, RpcStatus);
if (FAILED(hr))
{
DBGMSG(DBG_WARN, ("RpcServerUseProtseqEpA (ncalrpc) = %u\n",RpcStatus));
}
if (!ImpersonatePrinterClient(hToken) && SUCCEEDED(hr))
{
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WINDOWS, GetLastError());
}
}
return hr;
}
/*++
Routine Name
ServerGetPolicy
Routine Description:
Gets a numeric policy value that was read from the server.
This can be called by providers(localspl).
The policy must be by the server read before initializing providers.
Arguments:
pszPolicyName - policy name
pulValue - pointer to numeric value
Return Value:
HRESULT
--*/
HRESULT
ServerGetPolicy(
IN PCWSTR pszPolicyName,
IN ULONG* pulValue
)
{
HRESULT hr;
ULONG PolicyValue;
ULONG Index;
struct
{
PCWSTR pszName;
ULONG ulValue;
} PolicyTable[] =
{
{szRegisterSpoolerRemoteRpcEndPoint, gNamedPipeState.ePolicyValue},
{NULL , 0}
};
hr = (pulValue && pszPolicyName) ? S_OK : E_POINTER;
if (SUCCEEDED(hr))
{
hr = E_INVALIDARG;
for (Index = 0; PolicyTable[Index].pszName ; Index++)
{
if (_wcsicmp(pszPolicyName, szRegisterSpoolerRemoteRpcEndPoint) == 0)
{
PolicyValue = PolicyTable[Index].ulValue;
hr = S_OK;
break;
}
}
if (SUCCEEDED(hr))
{
*pulValue = PolicyValue;
}
}
return hr;
}