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.
646 lines
18 KiB
646 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
smbsecur.c
|
|
|
|
Abstract:
|
|
|
|
This module implements all functions related to enforce the SMB security signature on
|
|
transmitting and recieving SMB packages.
|
|
|
|
Revision History:
|
|
|
|
Yun Lin [YunLin] 23-December-1997
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
extern LONG NumOfBuffersForServerResponseInUse;
|
|
extern LIST_ENTRY ExchangesWaitingForServerResponseBuffer;
|
|
|
|
LIST_ENTRY SmbSecurityMdlWaitingExchanges;
|
|
|
|
NTSTATUS
|
|
SmbCeCheckMessageLength(
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
IN PVOID pTsdu, // pointer describing this TSDU, typically a lump of bytes
|
|
OUT PULONG pMessageLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calculates the server message length based on the SMB response command and data.
|
|
|
|
Arguments:
|
|
|
|
BytesIndicated - the bytes that are present in the indication.
|
|
|
|
BytesAvailable - the total data available
|
|
|
|
pTsdu - the data
|
|
|
|
pDataBufferSize - the length of the buffer
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS -
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
UCHAR SmbCommand;
|
|
PGENERIC_ANDX pSmbBuffer;
|
|
PSMB_HEADER pSmbHeader = (PSMB_HEADER)pTsdu;
|
|
ULONG ByteCount;
|
|
LONG WordCount;
|
|
LONG ByteLeft = BytesIndicated - sizeof(SMB_HEADER);
|
|
|
|
if (ByteLeft < 0) {
|
|
return STATUS_INVALID_NETWORK_RESPONSE;
|
|
}
|
|
|
|
*pMessageLength = sizeof(SMB_HEADER);
|
|
|
|
SmbCommand = pSmbHeader->Command;
|
|
|
|
pSmbBuffer = (PGENERIC_ANDX)(pSmbHeader + 1);
|
|
|
|
do {
|
|
|
|
switch (SmbCommand) {
|
|
case SMB_COM_LOCKING_ANDX:
|
|
case SMB_COM_WRITE_ANDX:
|
|
case SMB_COM_SESSION_SETUP_ANDX:
|
|
case SMB_COM_LOGOFF_ANDX:
|
|
case SMB_COM_TREE_CONNECT_ANDX:
|
|
case SMB_COM_NT_CREATE_ANDX:
|
|
case SMB_COM_OPEN_ANDX:
|
|
|
|
SmbCommand = pSmbBuffer->AndXCommand;
|
|
|
|
*pMessageLength = pSmbBuffer->AndXOffset;
|
|
pSmbBuffer = (PGENERIC_ANDX)((PUCHAR)pTsdu + pSmbBuffer->AndXOffset);
|
|
|
|
break;
|
|
|
|
case SMB_COM_READ_ANDX:
|
|
{
|
|
PRESP_READ_ANDX ReadAndX = (PRESP_READ_ANDX)pSmbBuffer;
|
|
|
|
WordCount = (ULONG)pSmbBuffer->WordCount;
|
|
|
|
if (ReadAndX->DataLengthHigh > 0) {
|
|
ByteCount = ReadAndX->DataLengthHigh << 16;
|
|
ByteCount += ReadAndX->DataLength;
|
|
} else {
|
|
ByteCount = *(PUSHORT)((PCHAR)pSmbBuffer + 1 + WordCount*sizeof(USHORT));
|
|
}
|
|
|
|
*pMessageLength += (WordCount+1)*sizeof(USHORT) + ByteCount + 1;
|
|
SmbCommand = SMB_COM_NO_ANDX_COMMAND;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
|
|
WordCount = (ULONG)pSmbBuffer->WordCount;
|
|
|
|
if (ByteLeft > (signed)sizeof(USHORT)*WordCount) {
|
|
ByteCount = *(PUSHORT)((PCHAR)pSmbBuffer + 1 + WordCount*sizeof(USHORT));
|
|
} else {
|
|
ByteCount = 0;
|
|
}
|
|
|
|
*pMessageLength += (WordCount+1)*sizeof(USHORT) + ByteCount + 1;
|
|
SmbCommand = SMB_COM_NO_ANDX_COMMAND;
|
|
}
|
|
|
|
ByteLeft = BytesIndicated - *pMessageLength;
|
|
|
|
if (ByteLeft < 0) {
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
break;
|
|
}
|
|
} while (SmbCommand != SMB_COM_NO_ANDX_COMMAND);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmbCeSyncExchangeForSecuritySignature(
|
|
PSMB_EXCHANGE pExchange
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines puts the exchange on the list waiting for the previous extended session
|
|
setup to finish in order to serialize the requests sent to the server with security
|
|
signature enabled.
|
|
Arguments:
|
|
|
|
pExchange - the smb exchange
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the exchange can be initiated.
|
|
STATUS_PENDING - the exchange can be resumed after the extended session setup finishes
|
|
|
|
Other Status codes correspond to error situations.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
PSMBCEDB_SESSION_ENTRY pSessionEntry;
|
|
KEVENT SmbCeSynchronizationEvent;
|
|
PSMBCEDB_REQUEST_ENTRY pRequestEntry = NULL;
|
|
|
|
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
|
pSessionEntry = SmbCeGetExchangeSessionEntry(pExchange);
|
|
|
|
if (pSessionEntry->SessionRecoverInProgress ||
|
|
pServerEntry->SecuritySignaturesEnabled &&
|
|
!pServerEntry->SecuritySignaturesActive) {
|
|
//
|
|
// if security signature is enabled and not yet turned on, exchange should wait for
|
|
// outstanding extended session setup to finish before resume in order to avoid index mismatch.
|
|
//
|
|
RxLog(("** Syncing xchg %lx for sess recovery.\n",pExchange));
|
|
|
|
if (!pSessionEntry->SessionRecoverInProgress) {
|
|
if (!pServerEntry->ExtSessionSetupInProgress) {
|
|
if (pExchange->Type == EXTENDED_SESSION_SETUP_EXCHANGE) {
|
|
// if this is the first extended session setup, let it proceed
|
|
pServerEntry->ExtSessionSetupInProgress = TRUE;
|
|
}
|
|
return Status;
|
|
}
|
|
} else {
|
|
if (pExchange->Type == EXTENDED_SESSION_SETUP_EXCHANGE) {
|
|
// if this is the extended session setup, let it proceed
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we are performing an operation that does not attempt reconnects, it will
|
|
// not recover from the disconnect/lack of session. We should simply abort here.
|
|
// However, if we are retrying because of a session expiry (indicated by a RECOVER
|
|
// flag on the session entry.), then we retry regardless of the exchange flag.
|
|
//
|
|
if( ! ( pSessionEntry->Header.State == SMBCEDB_RECOVER ||
|
|
pSessionEntry->SessionRecoverInProgress ) ) {
|
|
|
|
if( ( pExchange->SmbCeFlags & SMBCE_EXCHANGE_ATTEMPT_RECONNECTS ) == 0 ) {
|
|
return STATUS_CONNECTION_DISCONNECTED;
|
|
}
|
|
}
|
|
|
|
pRequestEntry = (PSMBCEDB_REQUEST_ENTRY)SmbMmAllocateObject(SMBCEDB_OT_REQUEST);
|
|
|
|
if (pRequestEntry != NULL) {
|
|
pRequestEntry->Request.pExchange = pExchange;
|
|
|
|
SmbCeIncrementPendingLocalOperations(pExchange);
|
|
SmbCeAddRequestEntry(&pServerEntry->SecuritySignatureSyncRequests,pRequestEntry);
|
|
|
|
if (pExchange->pSmbCeSynchronizationEvent != NULL) {
|
|
Status = STATUS_PENDING;
|
|
} else {
|
|
KeInitializeEvent(
|
|
&SmbCeSynchronizationEvent,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
pExchange->pSmbCeSynchronizationEvent = &SmbCeSynchronizationEvent;
|
|
|
|
SmbCeReleaseResource();
|
|
|
|
KeWaitForSingleObject(
|
|
&SmbCeSynchronizationEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
SmbCeAcquireResource();
|
|
pExchange->pSmbCeSynchronizationEvent = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RxLog(("** Recovered Sess for xchg %lx\n",pExchange));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
SmbInitializeSmbSecuritySignature(
|
|
IN OUT PSMBCE_SERVER Server,
|
|
IN PUCHAR SessionKey,
|
|
IN PUCHAR ChallengeResponse,
|
|
IN ULONG ChallengeResponseLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the security signature generator for a session by calling MD5Update
|
|
on the session key, challenge response
|
|
|
|
Arguments:
|
|
|
|
SessionKey - Either the LM or NT session key, depending on which
|
|
password was used for authentication, must be at least 16 bytes
|
|
ChallengeResponse - The challenge response used for authentication, must
|
|
be at least 24 bytes
|
|
|
|
--*/
|
|
{
|
|
//DbgPrint( "MRxSmb: Initialize Security Signature Intermediate Contex\n");
|
|
|
|
RtlZeroMemory(&Server->SmbSecuritySignatureIntermediateContext, sizeof(MD5_CTX));
|
|
MD5Init(&Server->SmbSecuritySignatureIntermediateContext);
|
|
|
|
if (SessionKey != NULL) {
|
|
MD5Update(&Server->SmbSecuritySignatureIntermediateContext,
|
|
(PCHAR)SessionKey,
|
|
MSV1_0_USER_SESSION_KEY_LENGTH);
|
|
}
|
|
|
|
MD5Update(&Server->SmbSecuritySignatureIntermediateContext,
|
|
(PCHAR)ChallengeResponse,
|
|
ChallengeResponseLength);
|
|
|
|
Server->SmbSecuritySignatureIndex = 0;
|
|
}
|
|
|
|
BOOLEAN DumpSecuritySignature = FALSE;
|
|
|
|
NTSTATUS
|
|
SmbAddSmbSecuritySignature(
|
|
IN PSMBCE_SERVER Server,
|
|
IN OUT PMDL Mdl,
|
|
IN OUT ULONG *ServerIndex,
|
|
IN ULONG SendLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates the next security signature
|
|
|
|
Arguments:
|
|
|
|
WorkContext - the context to sign
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
MD5_CTX Context;
|
|
PSMB_HEADER Smb;
|
|
PCHAR SysAddress;
|
|
ULONG MessageLength = 0;
|
|
|
|
Smb = MmGetSystemAddressForMdlSafe(Mdl,LowPagePriority);
|
|
|
|
if (Smb == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//SmbPutUshort(&Smb->Gid,(USHORT)Server->SmbSecuritySignatureIndex+1);
|
|
|
|
SmbPutUlong(Smb->SecuritySignature,Server->SmbSecuritySignatureIndex);
|
|
*ServerIndex = Server->SmbSecuritySignatureIndex+1; //Index of server response
|
|
|
|
RtlZeroMemory(Smb->SecuritySignature + sizeof(ULONG),
|
|
SMB_SECURITY_SIGNATURE_LENGTH-sizeof(ULONG));
|
|
|
|
//
|
|
// Start out with our initial context
|
|
//
|
|
RtlCopyMemory( &Context, &Server->SmbSecuritySignatureIntermediateContext, sizeof( Context ) );
|
|
|
|
//
|
|
// Compute the signature for the SMB we're about to send
|
|
//
|
|
do {
|
|
SysAddress = MmGetSystemAddressForMdlSafe(Mdl,LowPagePriority);
|
|
|
|
if (SysAddress == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (Mdl->ByteCount >= SendLength) {
|
|
MD5Update(&Context, SysAddress, SendLength);
|
|
MessageLength += SendLength;
|
|
SendLength = 0;
|
|
ASSERT(Mdl->Next == NULL);
|
|
break;
|
|
} else {
|
|
MD5Update(&Context, SysAddress, Mdl->ByteCount);
|
|
SendLength -= Mdl->ByteCount;
|
|
MessageLength += Mdl->ByteCount;
|
|
ASSERT(Mdl->Next != NULL);
|
|
}
|
|
} while( (Mdl = Mdl->Next) != NULL );
|
|
|
|
MD5Final( &Context );
|
|
|
|
|
|
// Put the signature into the SMB
|
|
|
|
RtlCopyMemory(
|
|
Smb->SecuritySignature,
|
|
Context.digest,
|
|
SMB_SECURITY_SIGNATURE_LENGTH
|
|
);
|
|
|
|
if (DumpSecuritySignature) {
|
|
DbgPrint("Add Signature: index %u length %u\n", *ServerIndex-1,MessageLength);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
SmbDumpSignatureError(
|
|
IN PSMB_EXCHANGE pExchange,
|
|
IN PUCHAR ExpectedSignature,
|
|
IN PUCHAR ActualSignature,
|
|
IN ULONG Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Print the mismatched signature information to the debugger
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
PWCHAR p;
|
|
DWORD i;
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry = (PSMBCEDB_SERVER_ENTRY)SmbCeGetExchangeServerEntry(pExchange);
|
|
|
|
//
|
|
// Security Signature Mismatch!
|
|
//
|
|
|
|
//DbgPrint("MRXSMB: Bad security signature from %wZ ", &pServerEntry->Name);
|
|
|
|
DbgPrint("\n\t Wanted: ");
|
|
for( i = 0; i < SMB_SECURITY_SIGNATURE_LENGTH; i++ ) {
|
|
DbgPrint( "%X ", ExpectedSignature[i] & 0xff );
|
|
}
|
|
DbgPrint("\n\tReceived: ");
|
|
for( i = 0; i < SMB_SECURITY_SIGNATURE_LENGTH; i++ ) {
|
|
DbgPrint( "%X ", ActualSignature[i] & 0xff );
|
|
}
|
|
DbgPrint("\n\tLength %u, Expected Index Number %X\n", Length, pExchange->SmbSecuritySignatureIndex);
|
|
}
|
|
|
|
VOID
|
|
SmbCheckSecuritySignature(
|
|
IN PSMB_EXCHANGE pExchange,
|
|
IN PSMBCE_SERVER Server,
|
|
IN ULONG MessageLength,
|
|
IN PVOID pBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks whether the security signature on the server response matches the one that is
|
|
calculated on the client machine.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
A BOOLEAN value is returned to indicated whether the security signature matches.
|
|
|
|
--*/
|
|
{
|
|
MD5_CTX *Context = &pExchange->MD5Context;
|
|
PCHAR SavedSignature = pExchange->ResponseSignature;
|
|
PSMB_HEADER Smb = (PSMB_HEADER)pBuffer;
|
|
ULONG ServerIndex;
|
|
BOOLEAN Correct;
|
|
|
|
//
|
|
// Initialize the Context
|
|
//
|
|
RtlCopyMemory(Context, &Server->SmbSecuritySignatureIntermediateContext, sizeof(MD5_CTX));
|
|
|
|
//
|
|
// Save the signature that's presently in the SMB
|
|
//
|
|
RtlCopyMemory( SavedSignature, Smb->SecuritySignature, sizeof(CHAR) * SMB_SECURITY_SIGNATURE_LENGTH);
|
|
|
|
//
|
|
// Put the correct (expected) signature index into the buffer
|
|
//
|
|
SmbPutUlong( Smb->SecuritySignature, pExchange->SmbSecuritySignatureIndex );
|
|
RtlZeroMemory( Smb->SecuritySignature + sizeof(ULONG),
|
|
SMB_SECURITY_SIGNATURE_LENGTH-sizeof(ULONG));
|
|
|
|
//
|
|
// Compute what the signature should be
|
|
//
|
|
MD5Update(Context, (PUCHAR)pBuffer, (UINT)MessageLength);
|
|
|
|
//
|
|
// Restore the signature that's presently in the SMB Header
|
|
//
|
|
RtlCopyMemory( Smb->SecuritySignature, SavedSignature, sizeof(CHAR)*SMB_SECURITY_SIGNATURE_LENGTH);
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SmbCheckSecuritySignaturePartial(
|
|
IN PSMB_EXCHANGE pExchange,
|
|
IN PSMBCE_SERVER Server,
|
|
IN ULONG DataLength,
|
|
IN PMDL Mdl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks whether the security signature on the server response matches the one that is
|
|
calculated on the client machine.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
A BOOLEAN value is returned to indicated whether the security signature matches.
|
|
|
|
--*/
|
|
{
|
|
MD5_CTX *Context = &pExchange->MD5Context;
|
|
PCHAR SavedSignature = pExchange->ResponseSignature;
|
|
ULONG ServerIndex;
|
|
BOOLEAN Correct;
|
|
PCHAR SysAddress;
|
|
ULONG MessageLength = 0;
|
|
|
|
do {
|
|
SysAddress = MmGetSystemAddressForMdlSafe(Mdl,LowPagePriority);
|
|
|
|
if (SysAddress == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (Mdl->ByteCount >= DataLength) {
|
|
MD5Update(Context, SysAddress, DataLength);
|
|
MessageLength += DataLength;
|
|
ASSERT(Mdl->Next == NULL);
|
|
break;
|
|
} else {
|
|
MD5Update(Context, SysAddress, Mdl->ByteCount);
|
|
MessageLength += Mdl->ByteCount;
|
|
ASSERT(Mdl->Next != NULL);
|
|
}
|
|
} while( (Mdl = Mdl->Next) != NULL );
|
|
|
|
MD5Final(Context);
|
|
|
|
//
|
|
// Now compare them!
|
|
//
|
|
if( RtlCompareMemory( Context->digest, SavedSignature, sizeof( SavedSignature ) ) !=
|
|
sizeof( SavedSignature ) ) {
|
|
|
|
//SmbDumpSignatureError(pExchange,
|
|
// Context.digest,
|
|
// SavedSignature,
|
|
// MessageLength);
|
|
|
|
//DbgPrint("MRXSMB: SS mismatch command %X, Length %X, Expected Index Number %X\n",
|
|
// Smb->Command, MessageLength, pExchange->SmbSecuritySignatureIndex);
|
|
//DbgPrint(" server send length %X, mdl length %X index %X\n",
|
|
// SmbGetUshort(&Smb->PidHigh), SmbGetUshort(&Smb->Pid), SmbGetUshort(&Smb->Gid));
|
|
|
|
//DbgBreakPoint();
|
|
|
|
//SmbCeTransportDisconnectIndicated(pExchange->SmbCeContext.pServerEntry);
|
|
|
|
//RxLogFailure(
|
|
// MRxSmbDeviceObject,
|
|
// NULL,
|
|
// EVENT_RDR_SECURITY_SIGNATURE_MISMATCH,
|
|
// STATUS_UNSUCCESSFUL);
|
|
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
SmbCheckSecuritySignatureWithMdl(
|
|
IN PSMB_EXCHANGE pExchange,
|
|
IN PSMBCE_SERVER Server,
|
|
IN ULONG DataLength,
|
|
IN PMDL Mdl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks whether the security signature on the server response matches the one that is
|
|
calculated on the client machine.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
A BOOLEAN value is returned to indicated whether the security signature matches.
|
|
|
|
--*/
|
|
{
|
|
MD5_CTX *Context = &pExchange->MD5Context;
|
|
PCHAR SavedSignature = pExchange->ResponseSignature;
|
|
ULONG ServerIndex;
|
|
BOOLEAN Correct;
|
|
PCHAR SysAddress;
|
|
ULONG MessageLength = 0;
|
|
|
|
ASSERT(Mdl->Next == NULL);
|
|
SysAddress = MmGetSystemAddressForMdlSafe(Mdl,LowPagePriority);
|
|
|
|
if (SysAddress == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
SmbCheckSecuritySignature(pExchange,
|
|
Server,
|
|
DataLength,
|
|
SysAddress);
|
|
|
|
MD5Final(&pExchange->MD5Context);
|
|
|
|
if (RtlCompareMemory( Context->digest, pExchange->ResponseSignature, SMB_SECURITY_SIGNATURE_LENGTH*sizeof(CHAR)) !=
|
|
SMB_SECURITY_SIGNATURE_LENGTH*sizeof(CHAR)) {
|
|
PSMB_HEADER Smb = (PSMB_HEADER)SysAddress;
|
|
|
|
//SmbDumpSignatureError(pExchange,
|
|
// Context.digest,
|
|
// SavedSignature,
|
|
// MessageLength);
|
|
|
|
#if DBG
|
|
DbgPrint("MRXSMB: SS mismatch command %X, Length %X, Expected Index Number %X\n",
|
|
Smb->Command, MessageLength, pExchange->SmbSecuritySignatureIndex);
|
|
DbgPrint(" server send length %X, mdl length %X index %X\n",
|
|
SmbGetUshort(&Smb->PidHigh), SmbGetUshort(&Smb->Pid), SmbGetUshort(&Smb->Gid));
|
|
#endif
|
|
//DbgBreakPoint();
|
|
|
|
//SmbCeTransportDisconnectIndicated(pExchange->SmbCeContext.pServerEntry);
|
|
|
|
//RxLogFailure(
|
|
// MRxSmbDeviceObject,
|
|
// NULL,
|
|
// EVENT_RDR_SECURITY_SIGNATURE_MISMATCH,
|
|
// STATUS_UNSUCCESSFUL);
|
|
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|