1239 lines
30 KiB
C
1239 lines
30 KiB
C
/*++
|
|
|
|
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;
|
|
BOOL fReleasedCredentials = FALSE;
|
|
PLOGON pLogon;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Note: If you are holding the credential resource coming in, then you
|
|
// need to be at the head of the queue.
|
|
//
|
|
|
|
//
|
|
// 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, NDS_BUFFER_SIZE, &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 );
|
|
|
|
//
|
|
// If you hold the credential lock, this is the time to let go of it or
|
|
// we might deadlock. We can reclaim it after we are at the head of the
|
|
// new SCB queue
|
|
//
|
|
|
|
if (BooleanFlagOn (pIrpContext->Flags, IRP_FLAG_HAS_CREDENTIAL_LOCK)) {
|
|
|
|
PSCB pScb;
|
|
|
|
pScb = pIrpContext->pNpScb->pScb;
|
|
|
|
NwAcquireExclusiveRcb( &NwRcb, TRUE );
|
|
pLogon = FindUser( &pScb->UserUid, FALSE );
|
|
NwReleaseRcb( &NwRcb );
|
|
|
|
NwReleaseCredList( pLogon, pIrpContext );
|
|
fReleasedCredentials = TRUE;
|
|
}
|
|
|
|
NwDequeueIrpContext( pIrpContext, FALSE );
|
|
|
|
Status = CreateScb( &Scb,
|
|
pIrpContext,
|
|
&ReferredServer,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
FALSE );
|
|
|
|
if (fReleasedCredentials == TRUE) {
|
|
|
|
//
|
|
// You have to be at the head of the queue before you
|
|
// grab the resource
|
|
//
|
|
|
|
if ( pIrpContext->pNpScb->Requests.Flink != &pIrpContext->NextRequest )
|
|
{
|
|
NwAppendToQueueAndWait( pIrpContext );
|
|
}
|
|
NwAcquireExclusiveCredList( pLogon, pIrpContext );
|
|
}
|
|
|
|
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, NDS_BUFFER_SIZE, &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, NDS_BUFFER_SIZE, 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;
|
|
|
|
case -630: // We get this back for bogus resolve
|
|
// name calls. Glenn prefers this error.
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
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,
|
|
NDS_BUFFER_SIZE,
|
|
&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;
|
|
|
|
}
|