diff options
Diffstat (limited to 'private/nw/rdr/ndsread.c')
-rw-r--r-- | private/nw/rdr/ndsread.c | 1190 |
1 files changed, 1190 insertions, 0 deletions
diff --git a/private/nw/rdr/ndsread.c b/private/nw/rdr/ndsread.c new file mode 100644 index 000000000..189fc9f1c --- /dev/null +++ b/private/nw/rdr/ndsread.c @@ -0,0 +1,1190 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + NdsRead.c + +Abstract: + + This module implements the NDS read and request routines called + by the redirector natively and the support routines that go with + them. + +Author: + + Cory West [CoryWest] 23-Feb-1995 + +--*/ + +#include "Procs.h" + +#define Dbg (DEBUG_TRACE_NDS) + +#pragma alloc_text( PAGE, NdsResolveNameKm ) +#pragma alloc_text( PAGE, NdsReadStringAttribute ) +#pragma alloc_text( PAGE, NdsReadAttributesKm ) +#pragma alloc_text( PAGE, NdsCompletionCodetoNtStatus ) +#pragma alloc_text( PAGE, FreeNdsContext ) +#pragma alloc_text( PAGE, NdsPing ) +#pragma alloc_text( PAGE, NdsGetUserName ) +#pragma alloc_text( PAGE, NdsGetServerBasicName ) +#pragma alloc_text( PAGE, NdsGetServerName ) +#pragma alloc_text( PAGE, NdsReadPublicKey ) +#pragma alloc_text( PAGE, NdsCheckGroupMembership ) +#pragma alloc_text( PAGE, NdsAllocateLockedBuffer ) +#pragma alloc_text( PAGE, NdsFreeLockedBuffer ) + +NTSTATUS +NdsResolveNameKm ( + PIRP_CONTEXT pIrpContext, + IN PUNICODE_STRING puObjectName, + OUT DWORD *dwObjectId, + BOOLEAN AllowDsJump, + DWORD dwFlags +) +/*++ + +Description: + + This is a wrapper routine to the browser routine NdsResolveName + for kernel components that need to resolve NDS names. + +Arguments: + + pIrpContext - must point to the dir server that we should query + puObjectName - what we want to resolve + *dwObjectId - where to report the result + AllowDsJump - if we are referred to another dir server, can we jump? + +--*/ +{ + + NTSTATUS Status; + + PNWR_NDS_REQUEST_PACKET Rrp; + + PNDS_RESPONSE_RESOLVE_NAME Rsp; + LOCKED_BUFFER NdsRequestBuffer; + + PSCB Scb, OldScb; + UNICODE_STRING ReferredServer; + + PAGED_CODE(); + + // + // Prepare the request and response buffers. + // + + Rrp = ALLOCATE_POOL( PagedPool, NDS_BUFFER_SIZE ); + + if ( !Rrp ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = NdsAllocateLockedBuffer( &NdsRequestBuffer, NDS_BUFFER_SIZE ); + + if ( !NT_SUCCESS( Status ) ) { + FREE_POOL( Rrp ); + return Status; + } + + // + // Set up the request packet. + // + + RtlZeroMemory( Rrp, NDS_BUFFER_SIZE ); + + Rrp->Version = 0; + Rrp->Parameters.ResolveName.ObjectNameLength = puObjectName->Length; + Rrp->Parameters.ResolveName.ResolverFlags = dwFlags; + + RtlCopyMemory( Rrp->Parameters.ResolveName.ObjectName, + puObjectName->Buffer, + puObjectName->Length ); + + // + // Do the resolve. + // + + Status = NdsResolveName( pIrpContext, Rrp, &NdsRequestBuffer ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + Status = NdsCompletionCodetoNtStatus( &NdsRequestBuffer ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + Rsp = ( PNDS_RESPONSE_RESOLVE_NAME ) NdsRequestBuffer.pRecvBufferVa; + + if ( ( Rsp->RemoteEntry == RESOLVE_NAME_REFER_REMOTE ) && + ( AllowDsJump ) ) { + + // + // We need to queue this request to another server + // since this server doesn't have any details about + // the object. + // + + ReferredServer.Length = (USHORT) Rsp->ServerNameLength; + ReferredServer.MaximumLength = ReferredServer.Length; + ReferredServer.Buffer = Rsp->ReferredServer; + + OldScb = pIrpContext->pScb; + ASSERT( OldScb != NULL ); + + NwDequeueIrpContext( pIrpContext, FALSE ); + + Status = CreateScb( &Scb, + pIrpContext, + &ReferredServer, + NULL, + NULL, + NULL, + TRUE, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + // + // Since we've jumped servers, dereference the old host + // server. The new one was referenced in CreateScb(). + // + + NwDereferenceScb( OldScb->pNpScb ); + + } + + *dwObjectId = Rsp->EntryId; + +ExitWithCleanup: + + NdsFreeLockedBuffer( &NdsRequestBuffer ); + FREE_POOL( Rrp ); + return Status; + +} + +NTSTATUS +NdsReadStringAttribute( + PIRP_CONTEXT pIrpContext, + IN DWORD dwObjectId, + IN PUNICODE_STRING puAttributeName, + OUT PUNICODE_STRING puAttributeVal +) +/*++ + +Description: + + This is a wrapper routine to the browser routine NdsReadAttributes + for kernel components that need to read NDS string attributes. + +Arguments: + + pIrpContext - must point to the dir server that we should query + dwObjectId - oid of the object to query + puAttributeName - attribute that we want + puAttributeVal - value of the attribute + +--*/ +{ + + NTSTATUS Status; + PNWR_NDS_REQUEST_PACKET Rrp; + DWORD dwRequestSize, dwAttributeCount; + LOCKED_BUFFER NdsRequest; + + PAGED_CODE(); + + // + // Set up the request and response buffers. + // + + dwRequestSize = sizeof( NWR_NDS_REQUEST_PACKET ) + puAttributeName->Length; + + Rrp = ( PNWR_NDS_REQUEST_PACKET ) ALLOCATE_POOL( PagedPool, dwRequestSize ); + + if ( !Rrp ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE ); + + if ( !NT_SUCCESS( Status ) ) { + FREE_POOL( Rrp ); + return Status; + } + + // + // Prepare the request packet. + // + + RtlZeroMemory( (BYTE *)Rrp, dwRequestSize ); + + Rrp->Version = 0; + Rrp->Parameters.ReadAttribute.ObjectId = dwObjectId; + Rrp->Parameters.ReadAttribute.IterHandle = DUMMY_ITER_HANDLE; + Rrp->Parameters.ReadAttribute.AttributeNameLength = puAttributeName->Length; + + RtlCopyMemory( Rrp->Parameters.ReadAttribute.AttributeName, + puAttributeName->Buffer, + puAttributeName->Length ); + + // + // Make the request. + // + + Status = NdsReadAttributes( pIrpContext, Rrp, &NdsRequest ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + // + // Dig out the string attribute and return it. + // + + Status = ParseResponse( NULL, + NdsRequest.pRecvBufferVa, + NdsRequest.dwBytesWritten, + "G___D_S_T", + sizeof( DWORD ), // completion code + sizeof( DWORD ), // iter handle + sizeof( DWORD ), // info type + &dwAttributeCount, // attribute count + sizeof( DWORD ), // syntax id + NULL, // attribute name + sizeof( DWORD ), // number of values + puAttributeVal ); // attribute string + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + +ExitWithCleanup: + + FREE_POOL( Rrp ); + NdsFreeLockedBuffer( &NdsRequest ); + return Status; + +} + +NTSTATUS +NdsReadAttributesKm( + PIRP_CONTEXT pIrpContext, + IN DWORD dwObjectId, + IN PUNICODE_STRING puAttributeName, + IN OUT PLOCKED_BUFFER pNdsRequest +) +/*++ + +Description: + + This is a wrapper routine to the browser routine NdsReadAttributes + for kernel components that need to read NDS string attributes and + get back the raw response. + +Arguments: + + pIrpContext - must point to the dir server that we should query + dwObjectId - oid of the object to query + puAttributeName - attribute that we want + puAttributeVal - value of the attribute + +--*/ +{ + + NTSTATUS Status; + PNWR_NDS_REQUEST_PACKET Rrp; + DWORD dwRequestSize; + + PAGED_CODE(); + + // + // Set up the request. + // + + dwRequestSize = sizeof( NWR_NDS_REQUEST_PACKET ) + puAttributeName->Length; + + Rrp = ( PNWR_NDS_REQUEST_PACKET ) ALLOCATE_POOL( PagedPool, dwRequestSize ); + + if ( !Rrp ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory( (BYTE *)Rrp, dwRequestSize ); + + Rrp->Version = 0; + Rrp->Parameters.ReadAttribute.ObjectId = dwObjectId; + Rrp->Parameters.ReadAttribute.IterHandle = DUMMY_ITER_HANDLE; + Rrp->Parameters.ReadAttribute.AttributeNameLength = puAttributeName->Length; + + RtlCopyMemory( Rrp->Parameters.ReadAttribute.AttributeName, + puAttributeName->Buffer, + puAttributeName->Length ); + + Status = NdsReadAttributes( pIrpContext, Rrp, pNdsRequest ); + + FREE_POOL( Rrp ); + return Status; + +} + +// +// Frosting and other helper wrapper functions. +// + +NTSTATUS +NdsCompletionCodetoNtStatus( + IN PLOCKED_BUFFER pLockedBuffer +) +/*+++ + +Description: + + Translates the completion code of an NDS transaction into + an NTSTATUS error code. + +Arguments: + + pLockedBuffer - describes the locked reply buffer that contains + the response. + +---*/ +{ + NTSTATUS Status; + + PAGED_CODE(); + + // + // Try to get the completion code from the user's buffer. + // + + try { + + Status = *((DWORD *)pLockedBuffer->pRecvBufferVa); + + } except ( EXCEPTION_EXECUTE_HANDLER ) { + + return STATUS_UNSUCCESSFUL; + + } + + // + // Decode it. + // + + if ( Status != STATUS_SUCCESS ) { + + DebugTrace( 0, Dbg, "NDS Error Code: %08lx\n", Status ); + + switch ( Status ) { + + case -601: // No such entry. + case -602: // No such value. + case -603: // No such attribute. + case -607: // Illegal attribute. + case -610: // Illegal ds name. + + Status = STATUS_BAD_NETWORK_PATH; + break; + + // + // These may only come on a VERIFY_PASSWORD verb, which + // we do not support. I'm not sure, though. + // + + case -216: // Password too short. + case -215: // Duplicate password. + + Status = STATUS_PASSWORD_RESTRICTION; + break; + + case -222: // Expired password (and no grace logins left). + + Status = STATUS_PASSWORD_EXPIRED; + break; + + case -223: // Expired password; this is a successful grace login. + + Status = NWRDR_PASSWORD_HAS_EXPIRED; + break; + + case -639: // Incomplete authentication. + case -672: // No access. + case -677: // Invalid identity. + case -669: // Wrong password. + + Status = STATUS_WRONG_PASSWORD; + break; + + case -197: // Intruder lockout active. + case -220: // Account expired or disabled. + + Status = STATUS_ACCOUNT_DISABLED; + break; + + case -218: // Login time restrictions. + + Status = STATUS_LOGIN_TIME_RESTRICTION; + break; + + case -217: // Maximum logins exceeded. + + Status = STATUS_CONNECTION_COUNT_LIMIT; + break; + + default: + + Status = STATUS_UNSUCCESSFUL; + } + + } + + return Status; +} + +VOID +FreeNdsContext( + IN PNDS_SECURITY_CONTEXT pNdsSecContext +) +/*++ + +Routine Description: + + Free the referenced NDS context. + +--*/ +{ + PAGED_CODE(); + + // + // Make sure this is a valid thing to be mucking with. + // + + if ( !pNdsSecContext || + pNdsSecContext->ntc != NW_NTC_NDS_CREDENTIAL ) { + + DebugTrace( 0, Dbg, "FreeNdsContext didn't get an NDS context.\n", 0 ); + return; + } + + if ( pNdsSecContext->Credential ) { + FREE_POOL( pNdsSecContext->Credential ); + } + + if ( pNdsSecContext->Signature ) { + FREE_POOL( pNdsSecContext->Signature ); + } + + if ( pNdsSecContext->PublicNdsKey ) { + FREE_POOL( pNdsSecContext->PublicNdsKey ); + } + + if ( pNdsSecContext->Password.Buffer ) { + FREE_POOL( pNdsSecContext->Password.Buffer ); + } + + DebugTrace( 0, Dbg, "Freeing NDS security context at 0x%08lx\n", pNdsSecContext ); + + FREE_POOL( pNdsSecContext ); + + return; +} + +VOID +NdsPing( + IN PIRP_CONTEXT pIrpContext, + IN PSCB pScb +) +/*++ + +Routine Description: + + Examine the server for NDS support and record the NDS tree + name in the SCB for later reference. + +Routine Arguments: + + pIrpContext - A pointer to the IRP context for this transaction. + pScb - The SCB for the server. + +Return Value: + + NTSTATUS - Status of the operation. + +--*/ +{ + + NTSTATUS Status; + + OEM_STRING OemTreeName; + BYTE OemBuffer[NDS_TREE_NAME_LEN]; + + UNICODE_STRING TreeName; + WCHAR WBuffer[NDS_TREE_NAME_LEN]; + + UNICODE_STRING CredentialName; + + PAGED_CODE(); + + pScb->NdsTreeName.Length = 0; + + OemTreeName.Length = NDS_TREE_NAME_LEN; + OemTreeName.MaximumLength = NDS_TREE_NAME_LEN; + OemTreeName.Buffer = OemBuffer; + + Status = ExchangeWithWait( pIrpContext, + SynchronousResponseCallback, + "N", + NDS_REQUEST, // NDS Function 104 + NDS_PING ); // NDS Subfunction 1 + + if ( !NT_SUCCESS( Status ) ) { + return; + } + + // + // Pull out the padded NDS name + // + + Status = ParseResponse( pIrpContext, + pIrpContext->rsp, + pIrpContext->ResponseLength, + "N_r", + 2 * sizeof( DWORD ), + OemBuffer, + NDS_TREE_NAME_LEN ); + + if ( !NT_SUCCESS( Status ) ) { + return; + } + + // + // Strip off the padding and convert to unicode. + // + + while ( OemTreeName.Length > 0 && + OemBuffer[OemTreeName.Length - 1] == '_' ) { + OemTreeName.Length--; + } + + // + // Copy or munge the tree name, depending on the create type. + // + + if ( pIrpContext->Specific.Create.fExCredentialCreate ) { + + TreeName.Length = 0; + TreeName.MaximumLength = sizeof( WBuffer ); + TreeName.Buffer = WBuffer; + + Status = RtlOemStringToUnicodeString( &TreeName, + &OemTreeName, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + pScb->NdsTreeName.Length = 0; + return; + } + + Status = BuildExCredentialServerName( &TreeName, + pIrpContext->Specific.Create.puCredentialName, + &CredentialName ); + + if ( !NT_SUCCESS( Status ) ) { + return; + } + + RtlCopyUnicodeString( &pScb->NdsTreeName, &CredentialName ); + + FREE_POOL( CredentialName.Buffer ); + + } else { + + Status = RtlOemStringToUnicodeString( &pScb->NdsTreeName, + &OemTreeName, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + pScb->NdsTreeName.Length = 0; + return; + } + + } + + DebugTrace( 0, Dbg, "Nds Ping: Tree is ""%wZ""\n", &pScb->NdsTreeName); + return; + +} + +NTSTATUS +NdsGetUserName( + IN PIRP_CONTEXT pIrpContext, + IN DWORD dwUserOid, + OUT PUNICODE_STRING puUserName +) +/*++ + +Description: + + Get the fully distinguished name of the user referred to + by the provided oid. + +--*/ +{ + NTSTATUS Status; + LOCKED_BUFFER NdsRequest; + + PAGED_CODE(); + + // + // Allocate buffer space. + // + + Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE ); + + if ( !NT_SUCCESS( Status ) ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Make the request. + // + + Status = FragExWithWait( pIrpContext, + NDSV_READ_ENTRY_INFO, + &NdsRequest, + "DD", + 0, + dwUserOid ); + + if ( !NT_SUCCESS(Status) ) { + goto ExitWithCleanup; + } + + Status = NdsCompletionCodetoNtStatus( &NdsRequest ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + Status = ParseResponse( NULL, + NdsRequest.pRecvBufferVa, + NdsRequest.dwBytesWritten, + "G_St", + sizeof( NDS_RESPONSE_GET_OBJECT_INFO ), + NULL, + puUserName ); + + // + // We either got it or we didn't. + // + +ExitWithCleanup: + + NdsFreeLockedBuffer( &NdsRequest ); + return Status; + +} + +NTSTATUS +NdsGetServerBasicName( + IN PUNICODE_STRING pServerX500Name, + IN OUT PUNICODE_STRING pServerName +) { + + // + // Dig out the first component of the server's X.500 name. + // We count on the X500 prefix for the server object being "CN=", + // which might be unwise. + // + + USHORT usPrefixSize, usSrv; + + PAGED_CODE(); + + usPrefixSize = sizeof( "CN=" ) - sizeof( "" ); + usSrv = 0; + + if ( ( pServerX500Name->Buffer[0] != L'C' ) || + ( pServerX500Name->Buffer[1] != L'N' ) || + ( pServerX500Name->Buffer[2] != L'=' ) ) { + + DebugTrace( 0, Dbg, "NdsGetServerBasicName: Bad prefix.\n", 0 ); + return STATUS_INVALID_PARAMETER; + } + + if ( pServerX500Name->Length <= usPrefixSize ) { + + DebugTrace( 0, Dbg, "NdsGetServerBasicName: Bad string length.\n", 0 ); + return STATUS_INVALID_PARAMETER; + } + + pServerName->Buffer = pServerX500Name->Buffer + usPrefixSize; + pServerName->Length = 0; + + while ( ( usSrv < MAX_SERVER_NAME_LENGTH ) && + ( pServerName->Buffer[usSrv++] != L'.' ) ) { + + pServerName->Length += sizeof( WCHAR ); + } + + if ( usSrv == MAX_SERVER_NAME_LENGTH ) { + + DebugTrace( 0, Dbg, "NdsGetServerBasicName: Bad server name response.\n", 0 ); + return STATUS_BAD_NETWORK_PATH; + } + + pServerName->MaximumLength = pServerName->Length; + return STATUS_SUCCESS; + +} + +NTSTATUS +NdsGetServerName( + IN PIRP_CONTEXT pIrpContext, + OUT PUNICODE_STRING puServerName +) +/*++ + +Description: + + Get the fully distinguished name of the server that we + are connected to. + +--*/ +{ + + NTSTATUS Status; + LOCKED_BUFFER NdsRequest; + + PAGED_CODE(); + + // + // Make the request. + // + + Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE ); + + if ( !NT_SUCCESS( Status ) ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = FragExWithWait( pIrpContext, + NDSV_GET_SERVER_ADDRESS, + &NdsRequest, + NULL ); + + if ( !NT_SUCCESS(Status) ) { + goto ExitWithCleanup; + } + + Status = NdsCompletionCodetoNtStatus( &NdsRequest ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + // + // Get the server name from the response. + // + + Status = ParseResponse( NULL, + NdsRequest.pRecvBufferVa, + NdsRequest.dwBytesWritten, + "G_T", + sizeof( DWORD ), + puServerName ); + + if ( !NT_SUCCESS(Status) ) { + goto ExitWithCleanup; + } + +ExitWithCleanup: + + NdsFreeLockedBuffer( &NdsRequest ); + return Status; + +} + +NTSTATUS +NdsReadPublicKey( + IN PIRP_CONTEXT pIrpContext, + IN DWORD dwEntryId, + OUT BYTE *pPubKeyVal, + IN OUT DWORD *pPubKeyLen +) +/*++ + +Routine Description: + + Read the public key referenced by the given entry id. + +Routine Arguments: + + pIrpContext - The IRP context for this connection. + dwEntryId - The entry id of the key. + pPubKeyVal - The destination buffer for the public key. + pPubKeyLen - The length of the public key destination buffer. + +Return Value: + + The length of the key. + +--*/ +{ + NTSTATUS Status; + + LOCKED_BUFFER NdsRequest; + + PNWR_NDS_REQUEST_PACKET Rrp; + + DWORD dwAttrNameLen, dwAttrLen, dwRcvLen, dwNumEntries; + BYTE *pRcv; + + PAGED_CODE(); + + // + // Allocate and zero send and receive space. + // + + Rrp = ALLOCATE_POOL( PagedPool, NDS_BUFFER_SIZE ); + + if ( !Rrp ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE ); + + if ( !NT_SUCCESS( Status ) ) { + FREE_POOL( Rrp ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Fill in and prepare the request buffer. + // + + RtlZeroMemory( Rrp, NDS_BUFFER_SIZE ); + + Rrp->Version = 0; + Rrp->Parameters.ReadAttribute.ObjectId = dwEntryId; + Rrp->Parameters.ReadAttribute.IterHandle = DUMMY_ITER_HANDLE; + Rrp->Parameters.ReadAttribute.AttributeNameLength = + sizeof( PUBLIC_KEY_ATTRIBUTE ) - sizeof( WCHAR ); + + RtlCopyMemory( Rrp->Parameters.ReadAttribute.AttributeName, + PUBLIC_KEY_ATTRIBUTE, + sizeof( PUBLIC_KEY_ATTRIBUTE ) - sizeof( WCHAR ) ); + + // + // Do the exchange. + // + + Status = NdsReadAttributes( pIrpContext, + Rrp, + &NdsRequest ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + // + // Skip over the attribute header and name. + // + + Status = ParseResponse( NULL, + NdsRequest.pRecvBufferVa, + NdsRequest.dwBytesWritten, + "G_D", + 5 * sizeof( DWORD ), + &dwAttrNameLen ); + + if ( !NT_SUCCESS( Status ) ) { + + Status = STATUS_UNSUCCESSFUL; + goto ExitWithCleanup; + } + + // + // Skip over the part we've parsed and pull out the attribute. + // + + pRcv = (PBYTE)NdsRequest.pRecvBufferVa + + ( 6 * sizeof( DWORD ) ) + + ROUNDUP4(dwAttrNameLen); + + dwRcvLen = NdsRequest.dwBytesWritten - + ( 6 * sizeof( DWORD ) ) + + ROUNDUP4(dwAttrNameLen); + + Status = ParseResponse( NULL, + pRcv, + dwRcvLen, + "GDD", + &dwNumEntries, + &dwAttrLen ); + + if ( !NT_SUCCESS( Status ) || + dwNumEntries != 1 ) { + + Status = STATUS_UNSUCCESSFUL; + goto ExitWithCleanup; + } + + DebugTrace( 0, Dbg, "Public Key Length: %d\n", dwAttrLen ); + pRcv += ( 2 * sizeof( DWORD ) ); + + if ( dwAttrLen <= *pPubKeyLen ) { + + RtlCopyMemory( pPubKeyVal, pRcv, dwAttrLen ); + *pPubKeyLen = dwAttrLen; + Status = STATUS_SUCCESS; + + } else { + + DebugTrace( 0, Dbg, "Public key buffer is too small.\n", 0 ); + Status = STATUS_BUFFER_TOO_SMALL; + } + +ExitWithCleanup: + + NdsFreeLockedBuffer( &NdsRequest ); + FREE_POOL( Rrp ); + return Status; + +} + +NTSTATUS +NdsCheckGroupMembership( + PIRP_CONTEXT pIrpContext, + DWORD dwUserOid, + PUNICODE_STRING puGroupName +) { + + NTSTATUS Status; + UNICODE_STRING GroupListAttribute; + LOCKED_BUFFER NdsRequest; + + PNDS_RESPONSE_READ_ATTRIBUTE pAttributeResponse; + PNDS_ATTRIBUTE pAttribute; + PBYTE pAttribData; + DWORD dwAttribLength, dwCurrentLength; + DWORD dwNumAttributes, dwCurrentAttribute; + UNICODE_STRING Group; + USHORT GroupLength; + + PAGED_CODE(); + + RtlInitUnicodeString( &GroupListAttribute, GROUPS_ATTRIBUTE ); + + Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE ); + + if ( !NT_SUCCESS( Status ) ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Status = NdsReadAttributesKm( pIrpContext, + dwUserOid, + &GroupListAttribute, + &NdsRequest ); + + if ( !NT_SUCCESS( Status ) ) { + goto ExitWithCleanup; + } + + pAttributeResponse = ( PNDS_RESPONSE_READ_ATTRIBUTE ) NdsRequest.pRecvBufferVa; + ASSERT( pAttributeResponse->NumAttributes > 0 ); + + // + // Skip over the response header and walk down the attribute + // until we get to the data. This is a little clunky. + // + + pAttribute = ( PNDS_ATTRIBUTE ) ( pAttributeResponse + 1 ); + dwCurrentLength = sizeof( NDS_RESPONSE_READ_ATTRIBUTE ); + + dwAttribLength = ROUNDUP4( pAttribute->AttribNameLength ); + dwAttribLength += ( 2 * sizeof( DWORD ) ); + + // + // Make sure we don't walk past the end of the buffer because + // of a bad packet from the server. + // + + if ( ( dwCurrentLength + dwAttribLength ) > NDS_BUFFER_SIZE ) { + return STATUS_UNSUCCESSFUL; + } + + pAttribData = ( ( BYTE * )pAttribute ) + dwAttribLength; + dwCurrentLength += dwAttribLength; + + // + // This is DWORD aligned for four byte DWORDs. + // + + if ( ( NDS_BUFFER_SIZE - dwCurrentLength ) < sizeof( DWORD ) ) { + return STATUS_UNSUCCESSFUL; + } + + dwNumAttributes = * ( ( DWORD * ) pAttribData ); + + if ( dwNumAttributes == 0 ) { + Status = STATUS_UNSUCCESSFUL; + goto ExitWithCleanup; + } + + // + // Each attribute is an NDS string DWORD aligned. + // + + Status = STATUS_UNSUCCESSFUL; + + pAttribData += sizeof( DWORD ); + dwCurrentLength += sizeof( DWORD ); + + for ( dwCurrentAttribute = 0; + dwCurrentAttribute < dwNumAttributes ; + dwCurrentAttribute++ ) { + + Group.Length = Group.MaximumLength = + ( USHORT )( * ( ( DWORD * ) pAttribData ) ) - sizeof( WCHAR ); + Group.Buffer = ( PWCHAR ) ( pAttribData + sizeof( DWORD ) ); + + if ( ( Group.Length + dwCurrentLength ) > NDS_BUFFER_SIZE ) { + return STATUS_UNSUCCESSFUL; + } + + // + // Strip off the X500 prefix and the context. + // + + GroupLength = 0; + + while ( GroupLength < ( Group.Length / sizeof( WCHAR ) ) ) { + + if ( Group.Buffer[GroupLength++] == L'=' ) { + + Group.Buffer += 1; + Group.Length -= sizeof( WCHAR ); + Group.MaximumLength -= sizeof( WCHAR ); + + GroupLength = ( Group.Length / sizeof( WCHAR ) ); + } + + Group.Buffer += 1; + Group.Length -= sizeof( WCHAR ); + Group.MaximumLength -= sizeof( WCHAR ); + } + + GroupLength = 0; + + while ( GroupLength < ( Group.Length / sizeof( WCHAR ) ) ) { + + if ( Group.Buffer[GroupLength++] == L'.' ) { + Group.Length = ( GroupLength - 1 ) * sizeof( WCHAR ); + Group.MaximumLength = Group.Length; + break; + } + } + + if ( RtlEqualUnicodeString( puGroupName, &Group, TRUE ) ) { + + DebugTrace( 0, Dbg, "Group check for %wZ succeeded.\n", &Group ); + Status = STATUS_SUCCESS; + goto ExitWithCleanup; + } + + // + // Dig out the attribute size and process the next entry. + // + + dwAttribLength = ROUNDUP4( * ( ( DWORD * ) pAttribData ) ); + dwAttribLength += sizeof( DWORD ); + pAttribData += dwAttribLength; + dwCurrentLength += dwAttribLength; + + } + +ExitWithCleanup: + + NdsFreeLockedBuffer( &NdsRequest ); + return Status; +} + + +NTSTATUS +NdsAllocateLockedBuffer( + PLOCKED_BUFFER NdsRequest, + DWORD BufferSize +) +/*++ + +Description: + + Allocate a buffer for io. Lock it down and fill in the + buffer data structure that we pass around. + +--*/ +{ + + PAGED_CODE(); + + NdsRequest->pRecvBufferVa = ALLOCATE_POOL( PagedPool, BufferSize ); + + if ( !NdsRequest->pRecvBufferVa ) { + DebugTrace( 0, Dbg, "Couldn't allocate locked io buffer.\n", 0 ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + NdsRequest->dwRecvLen = BufferSize; + NdsRequest->pRecvMdl = ALLOCATE_MDL( NdsRequest->pRecvBufferVa, + BufferSize, + FALSE, + FALSE, + NULL ); + + if ( !NdsRequest->pRecvMdl ) { + DebugTrace( 0, Dbg, "Couldn't allocate mdl for locked io buffer.\n", 0 ); + FREE_POOL( NdsRequest->pRecvBufferVa ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + MmProbeAndLockPages( NdsRequest->pRecvMdl, + KernelMode, + IoWriteAccess ); + + return STATUS_SUCCESS; + +} + +NTSTATUS +NdsFreeLockedBuffer( + PLOCKED_BUFFER NdsRequest +) +/*++ + +Description: + + Free a buffer allocated for io. + +--*/ +{ + + PAGED_CODE(); + + MmUnlockPages( NdsRequest->pRecvMdl ); + FREE_MDL( NdsRequest->pRecvMdl ); + FREE_POOL( NdsRequest->pRecvBufferVa ); + return STATUS_SUCCESS; + +} |