diff options
Diffstat (limited to 'private/newsam/server/server.c')
-rw-r--r-- | private/newsam/server/server.c | 920 |
1 files changed, 920 insertions, 0 deletions
diff --git a/private/newsam/server/server.c b/private/newsam/server/server.c new file mode 100644 index 000000000..3b4ad2097 --- /dev/null +++ b/private/newsam/server/server.c @@ -0,0 +1,920 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + server.c + +Abstract: + + This file contains services related to the SAM "server" object. + + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + + +NTSTATUS +SamrConnect2( + IN PSAMPR_SERVER_NAME ServerName, + OUT SAMPR_HANDLE * ServerHandle, + IN ACCESS_MASK DesiredAccess + ) + +/*++ + +Routine Description: + + This service is the dispatch routine for SamConnect. It performs + an access validation to determine whether the caller may connect + to SAM for the access specified. If so, a context block is established. + This is different from the SamConnect call in that the entire server + name is passed instead of just the first character. + + +Arguments: + + ServerName - Name of the node this SAM reside on. Ignored by this + routine. + + ServerHandle - If the connection is successful, the value returned + via this parameter serves as a context handle to the openned + SERVER object. + + DesiredAccess - Specifies the accesses desired to the SERVER object. + + +Return Value: + + Status values returned by SamIConnect(). + + +--*/ +{ + BOOLEAN TrustedClient; + + + // + // If we ever want to support trusted remote clients, then the test + // for whether or not the client is trusted can be made here and + // TrustedClient set appropriately. For now, all remote clients are + // considered untrusted. + + TrustedClient = FALSE; + + return SamIConnect(ServerName, ServerHandle, DesiredAccess, TrustedClient ); + +} + + +NTSTATUS +SamrConnect( + IN PSAMPR_SERVER_NAME ServerName, + OUT SAMPR_HANDLE * ServerHandle, + IN ACCESS_MASK DesiredAccess + ) + +/*++ + +Routine Description: + + This service is the dispatch routine for SamConnect. It performs + an access validation to determine whether the caller may connect + to SAM for the access specified. If so, a context block is established + + +Arguments: + + ServerName - Name of the node this SAM reside on. Ignored by this + routine. The name contains only a single character. + + ServerHandle - If the connection is successful, the value returned + via this parameter serves as a context handle to the openned + SERVER object. + + DesiredAccess - Specifies the accesses desired to the SERVER object. + + +Return Value: + + Status values returned by SamIConnect(). + + +--*/ +{ + BOOLEAN TrustedClient; + + + // + // If we ever want to support trusted remote clients, then the test + // for whether or not the client is trusted can be made here and + // TrustedClient set appropriately. For now, all remote clients are + // considered untrusted. + + TrustedClient = FALSE; + + return SamIConnect(NULL, ServerHandle, DesiredAccess, TrustedClient ); + +} + + +NTSTATUS +SamIConnect( + IN PSAMPR_SERVER_NAME ServerName, + OUT SAMPR_HANDLE * ServerHandle, + IN ACCESS_MASK DesiredAccess, + IN BOOLEAN TrustedClient + ) + +/*++ + +Routine Description: + + This service is the dispatch routine for SamConnect. It performs + an access validation to determine whether the caller may connect + to SAM for the access specified. If so, a context block is established + + + NOTE: If the caller is trusted, then the DesiredAccess parameter may + NOT contain any Generic access types or MaximumAllowed. All + mapping must be done by the caller. + +Arguments: + + ServerName - Name of the node this SAM reside on. Ignored by this + routine. + + ServerHandle - If the connection is successful, the value returned + via this parameter serves as a context handle to the openned + SERVER object. + + DesiredAccess - Specifies the accesses desired to the SERVER object. + + TrustedClient - Indicates whether the client is known to be part of + the trusted computer base (TCB). If so (TRUE), no access validation + is performed and all requested accesses are granted. If not + (FALSE), then the client is impersonated and access validation + performed against the SecurityDescriptor on the SERVER object. + +Return Value: + + + STATUS_SUCCESS - The SERVER object has been successfully openned. + + STATUS_INSUFFICIENT_RESOURCES - The SAM server processes doesn't + have sufficient resources to process or accept another connection + at this time. + + Other values as may be returned from: + + NtAccessCheckAndAuditAlarm() + + +--*/ +{ + NTSTATUS NtStatus; + PSAMP_OBJECT Context; + + UNREFERENCED_PARAMETER( ServerName ); //Ignored by this routine + + // + // If the SAM server is not initialized, reject the connection. + // + + if (SampServiceState != SampServiceEnabled) { + + return(STATUS_INVALID_SERVER_STATE); + } + + SampAcquireReadLock(); + + + Context = SampCreateContext( SampServerObjectType, TrustedClient ); + + if (Context != NULL) { + + // + // The RootKey for a SERVER object is the root of the SAM database. + // This key should not be closed when the context is deleted. + // + + Context->RootKey = SampKey; + + // + // The rootkeyname has been initialized to NULL inside CreateContext. + // + + // + // Perform access validation ... + // + + NtStatus = SampValidateObjectAccess( + Context, //Context + DesiredAccess, //DesiredAccess + FALSE //ObjectCreation + ); + + + + // + // if we didn't pass the access test, then free up the context block + // and return the error status returned from the access validation + // routine. Otherwise, return the context handle value. + // + + if (!NT_SUCCESS(NtStatus)) { + SampDeleteContext( Context ); + } else { + (*ServerHandle) = Context; + } + + } else { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + return(NtStatus); + +} + + +NTSTATUS +SamrShutdownSamServer( + IN SAMPR_HANDLE ServerHandle + ) + +/*++ + +Routine Description: + + This service shuts down the SAM server. + + In the long run, this routine will perform an orderly shutdown. + In the short term, it is useful for debug purposes to shutdown + in a brute force un-orderly fashion. + +Arguments: + + ServerHandle - Received from a previous call to SamIConnect(). + +Return Value: + + STATUS_SUCCESS - The services completed successfully. + + + STATUS_ACCESS_DENIED - The caller doesn't have the appropriate access + to perform the requested operation. + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT ServerContext; + SAMP_OBJECT_TYPE FoundType; + + + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + // + // Validate type of, and access to server object. + // + + ServerContext = (PSAMP_OBJECT)ServerHandle; + NtStatus = SampLookupContext( + ServerContext, + SAM_SERVER_SHUTDOWN, // DesiredAccess + SampServerObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + + // + // Signal the event that will cut loose the main thread. + // The main thread will then exit - causing the walls to + // come tumbling down. + // + + IgnoreStatus = RpcMgmtStopServerListening(0); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + + + // + // De-reference the server object + // + + IgnoreStatus = SampDeReferenceContext( ServerContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // Free the write lock and roll-back the transaction + // + + IgnoreStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + return(NtStatus); + +} + + +NTSTATUS +SamrLookupDomainInSamServer( + IN SAMPR_HANDLE ServerHandle, + IN PRPC_UNICODE_STRING Name, + OUT PRPC_SID *DomainId + ) + +/*++ + +Routine Description: + + This service + +Arguments: + + ServerHandle - A context handle returned by a previous call + to SamConnect(). + + Name - contains the name of the domain to look up. + + DomainSid - Receives a pointer to a buffer containing the SID of + the domain. The buffer pointed to must be deallocated by the + caller using MIDL_user_free() when no longer needed. + + +Return Value: + + + STATUS_SUCCESS - The services completed successfully. + + STATUS_ACCESS_DENIED - The caller doesn't have the appropriate access + to perform the requested operation. + + STATUS_NO_SUCH_DOMAIN - The specified domain does not exist at this + server. + + + STATUS_INVALID_SERVER_STATE - Indicates the SAM server is currently + disabled. + + + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT ServerContext; + SAMP_OBJECT_TYPE FoundType; + ULONG i, SidLength; + BOOLEAN DomainFound; + PSID FoundSid; + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (DomainId != NULL); + ASSERT ((*DomainId) == NULL); + + + + ASSERT( Name != NULL ); + if (Name->Buffer == NULL) { + return(STATUS_INVALID_PARAMETER); + } + + + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + ServerContext = (PSAMP_OBJECT)ServerHandle; + NtStatus = SampLookupContext( + ServerContext, + SAM_SERVER_LOOKUP_DOMAIN, + SampServerObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + + + // + // Set our default completion status + // + + NtStatus = STATUS_NO_SUCH_DOMAIN; + + + // + // Search the list of defined domains for a match. + // + + DomainFound = FALSE; + for (i = 0; + (i<SampDefinedDomainsCount && (!DomainFound)); + i++ ) { + + if (RtlEqualDomainName(&SampDefinedDomains[i].ExternalName, (PUNICODE_STRING)Name) ) { + + + DomainFound = TRUE; + + + // + // Allocate and fill in the return buffer + // + + SidLength = RtlLengthSid( SampDefinedDomains[i].Sid ); + FoundSid = MIDL_user_allocate( SidLength ); + if (FoundSid != NULL) { + NtStatus = + RtlCopySid( SidLength, FoundSid, SampDefinedDomains[i].Sid ); + + if (!NT_SUCCESS(NtStatus) ) { + MIDL_user_free( FoundSid ); + NtStatus = STATUS_INTERNAL_ERROR; + } + + (*DomainId) = FoundSid; + } + + + NtStatus = STATUS_SUCCESS; + } + + } + + + + // + // De-reference the object + // + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampDeReferenceContext( ServerContext, FALSE ); + + } else { + + IgnoreStatus = SampDeReferenceContext( ServerContext, FALSE ); + } + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + + + return(NtStatus); +} + + +NTSTATUS +SamrEnumerateDomainsInSamServer( + IN SAMPR_HANDLE ServerHandle, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + OUT PULONG CountReturned + ) + +/*++ + +Routine Description: + + This API lists all the domains defined in the account database. + Since there may be more domains than can fit into a buffer, the + caller is provided with a handle that can be used across calls to + the API. On the initial call, EnumerationContext should point to a + SAM_ENUMERATE_HANDLE variable that is set to 0. + + If the API returns STATUS_MORE_ENTRIES, then the API should be + called again with EnumerationContext. When the API returns + STATUS_SUCCESS or any error return, the handle becomes invalid for + future use. + + This API requires SAM_SERVER_ENUMERATE_DOMAINS access to the + SamServer object. + +Arguments: + + ConnectHandle - Handle obtained from a previous SamConnect call. + + EnumerationContext - API specific handle to allow multiple calls + (see below). This is a zero based index. + + Buffer - Receives a pointer to the buffer where the information + is placed. The information returned is contiguous + SAM_RID_ENUMERATION data structures. However, the + RelativeId field of each of these structures is not valid. + This buffer must be freed when no longer needed using + SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + CountReturned - Number of entries returned. + +Return Value: + + STATUS_SUCCESS - The Service completed successfully, and there + are no addition entries. + + STATUS_MORE_ENTRIES - There are more entries, so call again. + This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have the access required + to enumerate the domains. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_SERVER_STATE - Indicates the SAM server is + currently disabled. + + + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + ULONG i; + PSAMP_OBJECT Context; + SAMP_OBJECT_TYPE FoundType; + ULONG TotalLength = 0; + ULONG NewTotalLength; + PSAMP_ENUMERATION_ELEMENT SampHead, NextEntry, NewEntry; + BOOLEAN LengthLimitReached = FALSE; + PSAMPR_RID_ENUMERATION ArrayBuffer; + ULONG ArrayBufferLength; + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (ServerHandle != NULL); + ASSERT (EnumerationContext != NULL); + ASSERT ( Buffer != NULL); + ASSERT ((*Buffer) == NULL); + ASSERT (CountReturned != NULL); + + + // + // Initialize the list of names being returned. + // This is a singly linked list. + // + + SampHead = NULL; + + + // + // Initialize the count returned + // + + (*CountReturned) = 0; + + + + + + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + Context = (PSAMP_OBJECT)ServerHandle; + NtStatus = SampLookupContext( + Context, + SAM_SERVER_ENUMERATE_DOMAINS, + SampServerObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + + // + // Enumerating domains is easy. We keep a list in memory. + // All we have to do is use the enumeration context as an + // index into the defined domains array. + // + + + + // + // Set our default completion status + // Note that this is a SUCCESS status code. + // That is NT_SUCCESS(STATUS_MORE_ENTRIES) will return TRUE. + + // + + NtStatus = STATUS_MORE_ENTRIES; + + + + // + // Search the list of defined domains for a match. + // + + for ( i = (ULONG)(*EnumerationContext); + ( (i < SampDefinedDomainsCount) && + (NT_SUCCESS(NtStatus)) && + (!LengthLimitReached) ); + i++ ) { + + + // + // See if there is room for the next name. If TotalLength + // is still zero then we haven't yet even gotten one name. + // We have to return at least one name even if it exceeds + // the length request. + // + + + NewTotalLength = TotalLength + + sizeof(UNICODE_STRING) + + (ULONG)SampDefinedDomains[i].ExternalName.Length + + sizeof(UNICODE_NULL); + + if ( (NewTotalLength < PreferedMaximumLength) || + (TotalLength == 0) ) { + + if (NewTotalLength > SAMP_MAXIMUM_MEMORY_TO_USE) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + + TotalLength = NewTotalLength; + (*CountReturned) += 1; + + // + // Room for this name as well. + // Allocate a new return list entry, and a buffer for the + // name. + // + + NewEntry = MIDL_user_allocate(sizeof(SAMP_ENUMERATION_ELEMENT)); + if (NewEntry == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + NewEntry->Entry.Name.Buffer = + MIDL_user_allocate( + (ULONG)SampDefinedDomains[i].ExternalName.Length + + sizeof(UNICODE_NULL) + ); + + if (NewEntry->Entry.Name.Buffer == NULL) { + MIDL_user_free(NewEntry); + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + // + // Copy the name into the return buffer + // + + RtlCopyMemory( NewEntry->Entry.Name.Buffer, + SampDefinedDomains[i].ExternalName.Buffer, + SampDefinedDomains[i].ExternalName.Length + ); + NewEntry->Entry.Name.Length = SampDefinedDomains[i].ExternalName.Length; + NewEntry->Entry.Name.MaximumLength = NewEntry->Entry.Name.Length + (USHORT)sizeof(UNICODE_NULL); + UnicodeTerminate((PUNICODE_STRING)(&NewEntry->Entry.Name)); + + + // + // The Rid field of the ENUMERATION_INFORMATION is not + // filled in for domains. + // Just for good measure, set it to zero. + // + + NewEntry->Entry.RelativeId = 0; + + + + // + // Now add this to the list of names to be returned. + // + + NewEntry->Next = (PSAMP_ENUMERATION_ELEMENT)SampHead; + SampHead = NewEntry; + } + + } + } + + } else { + + LengthLimitReached = TRUE; + + } + + } + + + + + if ( NT_SUCCESS(NtStatus) ) { + + // + // Set the enumeration context + // + + (*EnumerationContext) = (*EnumerationContext) + (*CountReturned); + + + + // + // If we are returning the last of the names, then change our + // status code to indicate this condition. + // + + if ( ((*EnumerationContext) >= SampDefinedDomainsCount) ) { + + NtStatus = STATUS_SUCCESS; + } + + + + + // + // Build a return buffer containing an array of the + // SAM_RID_ENUMERATIONs pointed to by another + // buffer containing the number of elements in that + // array. + // + + (*Buffer) = MIDL_user_allocate( sizeof(SAMPR_ENUMERATION_BUFFER) ); + + if ( (*Buffer) == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + (*Buffer)->EntriesRead = (*CountReturned); + + ArrayBufferLength = sizeof( SAM_RID_ENUMERATION ) * + (*CountReturned); + ArrayBuffer = MIDL_user_allocate( ArrayBufferLength ); + (*Buffer)->Buffer = ArrayBuffer; + + if ( ArrayBuffer == NULL) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + MIDL_user_free( (*Buffer) ); + + } else { + + // + // Walk the list of return entries, copying + // them into the return buffer + // + + NextEntry = SampHead; + i = 0; + while (NextEntry != NULL) { + + NewEntry = NextEntry; + NextEntry = NewEntry->Next; + + ArrayBuffer[i] = NewEntry->Entry; + i += 1; + + MIDL_user_free( NewEntry ); + } + + } + + } + } + + + + + if ( !NT_SUCCESS(NtStatus) ) { + + // + // Free the memory we've allocated + // + + NextEntry = SampHead; + while (NextEntry != NULL) { + + NewEntry = NextEntry; + NextEntry = NewEntry->Next; + + MIDL_user_free( NewEntry->Entry.Name.Buffer ); + MIDL_user_free( NewEntry ); + } + + (*EnumerationContext) = 0; + (*CountReturned) = 0; + (*Buffer) = NULL; + + } + + // + // De-reference the object + // Note that NtStatus could be STATUS_MORE_ENTRIES, which is a + // successful return code - we want to make sure we return that, + // without wiping it out here. + // + + if ( NtStatus == STATUS_SUCCESS ) { + + NtStatus = SampDeReferenceContext( Context, FALSE ); + + } else { + + IgnoreStatus = SampDeReferenceContext( Context, FALSE ); + } + } + + + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + + return(NtStatus); + +} |