summaryrefslogtreecommitdiffstats
path: root/private/rpcutil/server.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/rpcutil/server.c
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/rpcutil/server.c')
-rw-r--r--private/rpcutil/server.c694
1 files changed, 694 insertions, 0 deletions
diff --git a/private/rpcutil/server.c b/private/rpcutil/server.c
new file mode 100644
index 000000000..f20a48227
--- /dev/null
+++ b/private/rpcutil/server.c
@@ -0,0 +1,694 @@
+/*++
+
+Copyright (c) 1990,91 Microsoft Corporation
+
+Module Name:
+
+ RpcServ.c
+
+Abstract:
+
+ This file contains commonly used server-side RPC functions,
+ such as starting and stoping RPC servers.
+
+Author:
+
+ Dan Lafferty danl 09-May-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 09-May-1991 Danl
+ Created
+
+ 03-July-1991 JimK
+ Copied from a net-specific file.
+
+ 18-Feb-1992 Danl
+ Added support for multiple endpoints & interfaces per server.
+
+ 10-Nov-1993 Danl
+ Wait for RPC calls to complete before returning from
+ RpcServerUnregisterIf. Also, do a WaitServerListen after
+ calling StopServerListen (when the last server shuts down).
+ Now this is similar to RpcServer functions in netlib.
+
+ 29-Jun-1995 RichardW
+ Read an alternative ACL from a key in the registry, if one exists.
+ This ACL is used to protect the named pipe.
+
+--*/
+
+//
+// INCLUDES
+//
+
+// These must be included first:
+#include <nt.h> // DbgPrint
+#include <ntrtl.h> // DbgPrint
+#include <windef.h> // win32 typedefs
+#include <rpc.h> // rpc prototypes
+#include <ntrpcp.h>
+#include <nturtl.h> // needed for winbase.h
+#include <winbase.h> // LocalAlloc
+
+// These may be included in any order:
+#include <stdlib.h> // for wcscpy wcscat
+#include <tstr.h> // WCSSIZE
+
+#define NT_PIPE_PREFIX_W L"\\PIPE\\"
+#define NT_PIPE_SD_PREFIX_W L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\SecurePipeServers\\"
+
+static
+PWSTR RpcpSecurablePipes[] = {
+ L"eventlog" // Eventlog server
+ };
+
+//
+// GLOBALS
+//
+
+ static CRITICAL_SECTION RpcpCriticalSection;
+ static DWORD RpcpNumInstances;
+
+
+
+DWORD
+RpcpInitRpcServer(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the critical section used to protect the
+ global server handle and instance count.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+--*/
+{
+ InitializeCriticalSection(&RpcpCriticalSection);
+ RpcpNumInstances = 0;
+
+ return(0);
+}
+
+NTSTATUS
+RpcpReadSDFromRegistry(
+ IN LPWSTR InterfaceName,
+ OUT PSECURITY_DESCRIPTOR * pSDToUse)
+/*++
+
+Routine Description:
+
+ This function checks the registry in the magic place to see if an extra
+ ACL has been defined for the pipe being passed in. If there is one, it
+ is translated to a NP acl, then returned. If there isn't one, or if
+ something goes wrong, an NULL acl is returned.
+
+Arguments:
+
+ InterfaceName name of the pipe to check for, e.g. winreg, etc.
+
+ pSDToUse returned a pointer to the security decriptor to use.
+
+Return Value:
+
+ STATUS_SUCCESS,
+ STATUS_NO_MEMORY,
+ Possible other errors from registry apis.
+
+
+--*/
+{
+ HANDLE hKey;
+ OBJECT_ATTRIBUTES ObjAttr;
+ UNICODE_STRING UniString;
+ PWSTR PipeKey;
+ NTSTATUS Status;
+ PSECURITY_DESCRIPTOR pSD;
+ ULONG cbNeeded;
+ ACL_SIZE_INFORMATION AclSize;
+ ULONG AceIndex;
+ ACCESS_MASK NewMask;
+ PACCESS_ALLOWED_ACE pAce;
+ PACL pAcl;
+ BOOLEAN DaclPresent;
+ BOOLEAN DaclDefaulted;
+ UNICODE_STRING Interface;
+ UNICODE_STRING Allowed;
+ ULONG i;
+ BOOLEAN PipeNameOk;
+ PSECURITY_DESCRIPTOR pNewSD;
+ PACL pNewAcl;
+
+ *pSDToUse = NULL;
+
+ RtlInitUnicodeString( &Interface, InterfaceName );
+
+ PipeNameOk = FALSE;
+
+ for ( i = 0 ; i < sizeof( RpcpSecurablePipes ) / sizeof(PWSTR) ; i++ )
+ {
+ RtlInitUnicodeString( &Allowed, RpcpSecurablePipes[i] );
+
+ if ( RtlCompareUnicodeString( &Allowed, &Interface, TRUE ) == 0 )
+ {
+ PipeNameOk = TRUE;
+ break;
+ }
+ }
+
+ if ( PipeNameOk )
+ {
+
+ PipeKey = RtlAllocateHeap(RtlProcessHeap(), 0,
+ sizeof(NT_PIPE_SD_PREFIX_W) + WCSSIZE(InterfaceName) );
+
+ if (!PipeKey)
+ {
+ return(STATUS_NO_MEMORY);
+ }
+
+ wcscpy(PipeKey, NT_PIPE_SD_PREFIX_W);
+ wcscat(PipeKey, InterfaceName);
+
+ RtlInitUnicodeString(&UniString, PipeKey);
+
+ InitializeObjectAttributes( &ObjAttr,
+ &UniString,
+ OBJ_CASE_INSENSITIVE,
+ NULL, NULL);
+
+ Status = NtOpenKey( &hKey,
+ KEY_READ,
+ &ObjAttr);
+
+ RtlFreeHeap(RtlProcessHeap(), 0, PipeKey);
+
+ }
+ else
+ {
+ //
+ // This is not one of the interfaces that we allow to be secured
+ // in this fashion. Fake and error,
+ //
+
+ Status = STATUS_OBJECT_NAME_NOT_FOUND ;
+ }
+
+ //
+ // In general, most times we won't find this key
+ //
+
+ if (!NT_SUCCESS(Status))
+ {
+ if ((Status == STATUS_OBJECT_NAME_NOT_FOUND) ||
+ (Status == STATUS_OBJECT_PATH_NOT_FOUND) )
+ {
+ pSD = RtlAllocateHeap(RtlProcessHeap(), 0,
+ sizeof(SECURITY_DESCRIPTOR) );
+ if (pSD)
+ {
+ InitializeSecurityDescriptor( pSD,
+ SECURITY_DESCRIPTOR_REVISION );
+
+ if (SetSecurityDescriptorDacl (
+ pSD,
+ TRUE, // Dacl present
+ NULL, // NULL Dacl
+ FALSE // Not defaulted
+ ) )
+ {
+ *pSDToUse = pSD;
+ return(STATUS_SUCCESS);
+ }
+
+ RtlFreeHeap(RtlProcessHeap(), 0, pSD);
+
+ }
+ return(STATUS_NO_MEMORY);
+ }
+
+ return(Status);
+
+ }
+
+ //
+ // Son of a gun, someone has established security for this pipe.
+ //
+
+ pSD = NULL;
+
+ cbNeeded = 0;
+ Status = NtQuerySecurityObject(
+ hKey,
+ DACL_SECURITY_INFORMATION,
+ NULL,
+ 0,
+ &cbNeeded );
+
+ if (Status == STATUS_BUFFER_TOO_SMALL)
+ {
+ pSD = RtlAllocateHeap(RtlProcessHeap(), 0, cbNeeded);
+ if (pSD)
+ {
+ Status = NtQuerySecurityObject(
+ hKey,
+ DACL_SECURITY_INFORMATION,
+ pSD,
+ cbNeeded,
+ &cbNeeded );
+
+ //
+ // One way or the other, we are done with the key handle
+ //
+
+ NtClose(hKey);
+
+ if (NT_SUCCESS(Status))
+ {
+ //
+ // Now, the tricky part. There is no 1-1 mapping of Key
+ // permissions to Pipe permissions. So, we do it here.
+ // We walk the DACL, and examine each ACE. We build a new
+ // access mask for each ACE, and set the flags as follows:
+ //
+ // if (KEY_READ) GENERIC_READ
+ // if (KEY_WRITE) GENERIC_WRITE
+ //
+
+ Status = RtlGetDaclSecurityDescriptor(
+ pSD,
+ &DaclPresent,
+ &pAcl,
+ &DaclDefaulted);
+
+
+ //
+ // If this failed, or there is no DACL present, then
+ // we're in trouble.
+ //
+
+ if (!NT_SUCCESS(Status) || !DaclPresent)
+ {
+ goto GetSDFromKey_BadAcl;
+ }
+
+
+ Status = RtlQueryInformationAcl(pAcl,
+ &AclSize,
+ sizeof(AclSize),
+ AclSizeInformation);
+
+ if (!NT_SUCCESS(Status))
+ {
+ goto GetSDFromKey_BadAcl;
+ }
+
+ for (AceIndex = 0; AceIndex < AclSize.AceCount ; AceIndex++ )
+ {
+ NewMask = 0;
+ Status = RtlGetAce( pAcl,
+ AceIndex,
+ & pAce);
+
+ //
+ // We don't care what kind of ACE it is, since we
+ // are just mapping the access types, and the access
+ // mask is always at a constant position.
+ //
+
+ if (NT_SUCCESS(Status))
+ {
+ if ((pAce->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) &&
+ (pAce->Header.AceType != ACCESS_DENIED_ACE_TYPE))
+ {
+ //
+ // Must be an audit or random ACE type. Skip it.
+ //
+
+ continue;
+
+ }
+
+
+ if (pAce->Mask & KEY_READ)
+ {
+ NewMask |= GENERIC_READ;
+ }
+
+ if (pAce->Mask & KEY_WRITE)
+ {
+ NewMask |= GENERIC_WRITE;
+ }
+
+ pAce->Mask = NewMask;
+ }
+ else
+ {
+ //
+ // Panic: Bad ACL?
+ //
+
+ goto GetSDFromKey_BadAcl;
+ }
+
+ }
+
+ //
+ // BUGBUG: RPC does not understand self-relative SDs, so
+ // we have to turn this into an absolute for them to turn
+ // back into a self relative.
+ //
+
+ pNewSD = RtlAllocateHeap(RtlProcessHeap(), 0, cbNeeded);
+ if (!pNewSD)
+ {
+ goto GetSDFromKey_BadAcl;
+ }
+
+ InitializeSecurityDescriptor( pNewSD,
+ SECURITY_DESCRIPTOR_REVISION);
+
+ pNewAcl = (PACL) (((PUCHAR) pNewSD) +
+ sizeof(SECURITY_DESCRIPTOR) );
+
+ RtlCopyMemory(pNewAcl, pAcl, AclSize.AclBytesInUse);
+
+ SetSecurityDescriptorDacl(pNewSD, TRUE, pNewAcl, FALSE);
+
+ RtlFreeHeap(RtlProcessHeap(), 0, pSD);
+
+ *pSDToUse = pNewSD;
+ return(Status);
+ }
+ }
+ return(STATUS_NO_MEMORY);
+ }
+ else
+ {
+ //
+ // Failed to read SD:
+ //
+
+ NtClose(hKey);
+
+
+GetSDFromKey_BadAcl:
+
+ //
+ // Free the SD that we have allocated
+ //
+
+ if (pSD)
+ {
+ RtlFreeHeap(RtlProcessHeap(), 0, pSD);
+ }
+
+ //
+ // Key exists, but there is no security descriptor, or it is unreadable
+ // for whatever reason.
+ //
+
+ pSD = RtlAllocateHeap(RtlProcessHeap(), 0,
+ sizeof(SECURITY_DESCRIPTOR) );
+ if (pSD)
+ {
+ InitializeSecurityDescriptor( pSD,
+ SECURITY_DESCRIPTOR_REVISION );
+
+ if (SetSecurityDescriptorDacl (
+ pSD,
+ TRUE, // Dacl present
+ NULL, // NULL Dacl
+ FALSE // Not defaulted
+ ) )
+ {
+ *pSDToUse = pSD;
+ return(STATUS_SUCCESS);
+ }
+
+ }
+ return(STATUS_NO_MEMORY);
+
+ }
+
+
+}
+
+
+NTSTATUS
+RpcpAddInterface(
+ IN LPWSTR InterfaceName,
+ IN RPC_IF_HANDLE InterfaceSpecification
+ )
+
+/*++
+
+Routine Description:
+
+ Starts an RPC Server, adds the address (or port/pipe), and adds the
+ interface (dispatch table).
+
+Arguments:
+
+ InterfaceName - points to the name of the interface.
+
+ InterfaceSpecification - Supplies the interface handle for the
+ interface which we wish to add.
+
+Return Value:
+
+ NT_SUCCESS - Indicates the server was successfully started.
+
+ STATUS_NO_MEMORY - An attempt to allocate memory has failed.
+
+ Other - Status values that may be returned by:
+
+ RpcServerRegisterIf()
+ RpcServerUseProtseqEp()
+
+ , or any RPC error codes, or any windows error codes that
+ can be returned by LocalAlloc.
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+ LPWSTR Endpoint = NULL;
+
+ PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+ BOOL Bool;
+ NTSTATUS Status;
+
+ // We need to concatenate \pipe\ to the front of the interface name.
+
+ Endpoint = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(NT_PIPE_PREFIX_W) + WCSSIZE(InterfaceName));
+ if (Endpoint == 0) {
+ return(STATUS_NO_MEMORY);
+ }
+ wcscpy(Endpoint, NT_PIPE_PREFIX_W );
+ wcscat(Endpoint,InterfaceName);
+
+ //
+ // 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.
+ //
+ // BUGBUG: rpc should copy the security descriptor,
+ // Since it currently doesn't, simply allocate it for now and
+ // leave it around forever.
+ //
+
+ Status = RpcpReadSDFromRegistry(InterfaceName, &SecurityDescriptor);
+ if (!NT_SUCCESS(Status))
+ {
+ return(Status);
+ }
+
+ // Ignore the second argument for now.
+
+ RpcStatus = RpcServerUseProtseqEpW(L"ncacn_np", 10, Endpoint, SecurityDescriptor);
+
+ // If RpcpStartRpcServer and then RpcpStopRpcServer have already
+ // been called, the endpoint will have already been added but not
+ // removed (because there is no way to do it). If the endpoint is
+ // already there, it is ok.
+
+ if ( (RpcStatus != RPC_S_OK)
+ && (RpcStatus != RPC_S_DUPLICATE_ENDPOINT)) {
+
+#if DBG
+ DbgPrint("RpcServerUseProtseqW failed! rpcstatus = %u\n",RpcStatus);
+#endif
+ goto CleanExit;
+ }
+
+ RpcStatus = RpcServerRegisterIf(InterfaceSpecification, 0, 0);
+
+CleanExit:
+ if ( Endpoint != NULL ) {
+ LocalFree(Endpoint);
+ }
+ if ( SecurityDescriptor != NULL) {
+ LocalFree(SecurityDescriptor);
+ }
+
+ return( I_RpcMapWin32Status(RpcStatus) );
+}
+
+
+NTSTATUS
+RpcpStartRpcServer(
+ IN LPWSTR InterfaceName,
+ IN RPC_IF_HANDLE InterfaceSpecification
+ )
+
+/*++
+
+Routine Description:
+
+ Starts an RPC Server, adds the address (or port/pipe), and adds the
+ interface (dispatch table).
+
+Arguments:
+
+ InterfaceName - points to the name of the interface.
+
+ InterfaceSpecification - Supplies the interface handle for the
+ interface which we wish to add.
+
+Return Value:
+
+ NT_SUCCESS - Indicates the server was successfully started.
+
+ STATUS_NO_MEMORY - An attempt to allocate memory has failed.
+
+ Other - Status values that may be returned by:
+
+ RpcServerRegisterIf()
+ RpcServerUseProtseqEp()
+
+ , or any RPC error codes, or any windows error codes that
+ can be returned by LocalAlloc.
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+
+ EnterCriticalSection(&RpcpCriticalSection);
+
+ RpcStatus = RpcpAddInterface( InterfaceName,
+ InterfaceSpecification );
+
+ if ( RpcStatus != RPC_S_OK ) {
+ LeaveCriticalSection(&RpcpCriticalSection);
+ return( I_RpcMapWin32Status(RpcStatus) );
+ }
+
+ RpcpNumInstances++;
+
+ if (RpcpNumInstances == 1) {
+
+
+ // The first argument specifies the minimum number of threads to
+ // be created to handle calls; the second argument specifies the
+ // maximum number of concurrent calls allowed. The last argument
+ // indicates not to wait.
+
+ RpcStatus = RpcServerListen(1,12345, 1);
+ if ( RpcStatus == RPC_S_ALREADY_LISTENING ) {
+ RpcStatus = RPC_S_OK;
+ }
+ }
+
+ LeaveCriticalSection(&RpcpCriticalSection);
+ return( I_RpcMapWin32Status(RpcStatus) );
+}
+
+
+NTSTATUS
+RpcpDeleteInterface(
+ IN RPC_IF_HANDLE InterfaceSpecification
+ )
+
+/*++
+
+Routine Description:
+
+ Deletes the interface. This is likely
+ to be caused by an invalid handle. If an attempt to add the same
+ interface or address again, then an error will be generated at that
+ time.
+
+Arguments:
+
+ InterfaceSpecification - A handle for the interface that is to be removed.
+
+Return Value:
+
+ NERR_Success, or any RPC error codes that can be returned from
+ RpcServerUnregisterIf.
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+
+ RpcStatus = RpcServerUnregisterIf(InterfaceSpecification, 0, 1);
+
+ return( I_RpcMapWin32Status(RpcStatus) );
+}
+
+
+NTSTATUS
+RpcpStopRpcServer(
+ IN RPC_IF_HANDLE InterfaceSpecification
+ )
+
+/*++
+
+Routine Description:
+
+ Deletes the interface. This is likely
+ to be caused by an invalid handle. If an attempt to add the same
+ interface or address again, then an error will be generated at that
+ time.
+
+Arguments:
+
+ InterfaceSpecification - A handle for the interface that is to be removed.
+
+Return Value:
+
+ NERR_Success, or any RPC error codes that can be returned from
+ RpcServerUnregisterIf.
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+
+ RpcStatus = RpcServerUnregisterIf(InterfaceSpecification, 0, 1);
+ EnterCriticalSection(&RpcpCriticalSection);
+
+ RpcpNumInstances--;
+ if (RpcpNumInstances == 0) {
+ RpcMgmtStopServerListening(0);
+ RpcMgmtWaitServerListen();
+ }
+
+ LeaveCriticalSection(&RpcpCriticalSection);
+
+ return( I_RpcMapWin32Status(RpcStatus) );
+}