windows-nt/Source/XPSP1/NT/ds/win32/ntcrypto/fips/selfmac.c
2020-09-26 16:20:57 +08:00

789 lines
17 KiB
C

/////////////////////////////////////////////////////////////////////////////
// FILE : selfmac.c //
// DESCRIPTION : Code to do self MACing //
// AUTHOR : //
// HISTORY : //
// Nov 04 1999 jeffspel Added provider type checking //
// Mar 2000 kschutz Added stuff to make it work in kernel //
// //
// Copyright (C) 1999 Microsoft Corporation All Rights Reserved //
/////////////////////////////////////////////////////////////////////////////
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <zwapi.h>
#include <windows.h>
#ifdef KERNEL_MODE
#include <ntosp.h>
#else
#include <imagehlp.h>
#endif // KERNEL_MODE
#include <des.h>
#include <modes.h>
// MAC in file
typedef struct _MAC_STRUCT
{
ULONG CoolMac[2];
ULONG dwMACStructOffset;
UCHAR rgbMac[DES_BLOCKLEN];
ULONG dwImageCheckSumOffset;
} MAC_STRUCT;
#define MAC_STRING "COOL MAC "
static LPSTR g_pszMAC = MAC_STRING;
static MAC_STRUCT *g_pMACStruct;
// The function MACs the given bytes.
VOID
MACBytes(
IN DESTable *pDESKeyTable,
IN UCHAR *pbData,
IN ULONG cbData,
IN OUT UCHAR *pbTmp,
IN OUT ULONG *pcbTmp,
IN OUT UCHAR *pbMAC,
IN BOOLEAN fFinal
)
{
ULONG cb = cbData;
ULONG cbMACed = 0;
while (cb)
{
if ((cb + *pcbTmp) < DES_BLOCKLEN)
{
memcpy(pbTmp + *pcbTmp, pbData + cbMACed, cb);
*pcbTmp += cb;
break;
}
else
{
memcpy(pbTmp + *pcbTmp, pbData + cbMACed, DES_BLOCKLEN - *pcbTmp);
CBC(des, DES_BLOCKLEN, pbMAC, pbTmp, pDESKeyTable,
ENCRYPT, pbMAC);
cbMACed = cbMACed + (DES_BLOCKLEN - *pcbTmp);
cb = cb - (DES_BLOCKLEN - *pcbTmp);
*pcbTmp = 0;
}
}
}
#define CSP_TO_BE_MACED_CHUNK 512
// Given hFile, reads the specified number of bytes (cbToBeMACed) from the file
// and MACs these bytes. The function does this in chunks.
NTSTATUS
MACBytesOfFile(
IN HANDLE hFile,
IN ULONG cbToBeMACed,
IN DESTable *pDESKeyTable,
IN UCHAR *pbTmp,
IN ULONG *pcbTmp,
IN UCHAR *pbMAC,
IN BOOLEAN fNoMacing,
IN BOOLEAN fFinal
)
{
UCHAR rgbChunk[CSP_TO_BE_MACED_CHUNK];
ULONG cbRemaining = cbToBeMACed, cbToRead, cbBytesRead;
NTSTATUS Status = STATUS_SUCCESS;
#ifdef KERNEL_MODE
IO_STATUS_BLOCK IoStatusBlock;
#endif // END KERNEL/USER MODE CHECK
//
// loop over the file for the specified number of bytes
// updating the hash as we go.
//
while (cbRemaining > 0)
{
if (cbRemaining < CSP_TO_BE_MACED_CHUNK)
cbToRead = cbRemaining;
else
cbToRead = CSP_TO_BE_MACED_CHUNK;
#ifdef KERNEL_MODE
Status = ZwReadFile(hFile,
NULL,
NULL,
NULL,
&IoStatusBlock,
rgbChunk,
cbToRead,
NULL,
NULL);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
if (cbToRead != IoStatusBlock.Information)
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
cbBytesRead = cbToRead;
#else // USER MODE
if(!ReadFile(hFile,
rgbChunk,
cbToRead,
&cbBytesRead,
NULL))
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
if (cbBytesRead != cbToRead)
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
#endif // END KERNEL/USER MODE CHECK
if (!fNoMacing)
{
MACBytes(pDESKeyTable,
rgbChunk,
cbBytesRead,
pbTmp,
pcbTmp,
pbMAC,
fFinal);
}
cbRemaining -= cbToRead;
}
Ret:
return Status;
}
static UCHAR rgbMACDESKey[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
NTSTATUS
MACTheFile(
LPCWSTR pszImage,
ULONG cbImageCheckSumOffset,
ULONG cbMACStructOffset,
UCHAR *pbMAC
)
{
ULONG cbFileLen = 0,cbHighPart, cbBytesToMac;
HANDLE hFile = INVALID_HANDLE_VALUE;
DESTable DESKeyTable;
BYTE rgbTmp[DES_BLOCKLEN];
DWORD cbTmp = 0;
MAC_STRUCT TmpMacStruct;
NTSTATUS Status = STATUS_SUCCESS;
#ifdef KERNEL_MODE
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES ObjectAttribs;
IO_STATUS_BLOCK IoStatusBlock;
BOOLEAN fFileOpened = FALSE;
FILE_STANDARD_INFORMATION FileInformation;
#endif // END KERNEL/USER MODE CHECK
RtlZeroMemory(pbMAC, DES_BLOCKLEN);
RtlZeroMemory(rgbTmp, sizeof(rgbTmp));
RtlZeroMemory(&TmpMacStruct, sizeof(TmpMacStruct));
#ifdef KERNEL_MODE
//
// get file length - kernel mode version
//
RtlZeroMemory(&ObjectAttribs, sizeof(ObjectAttribs));
RtlInitUnicodeString( &ObjectName, pszImage );
InitializeObjectAttributes(
&ObjectAttribs,
&ObjectName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = ZwCreateFile(
&hFile,
SYNCHRONIZE | FILE_READ_DATA,
&ObjectAttribs,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ ,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
fFileOpened = TRUE;
Status = ZwQueryInformationFile(
hFile,
&IoStatusBlock,
&FileInformation,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
cbFileLen = FileInformation.EndOfFile.LowPart;
#else // USER MODE
//
// get file length - user mode version
//
if ((hFile = CreateFileW(
pszImage,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) == INVALID_HANDLE_VALUE)
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
cbFileLen = GetFileSize(hFile, &cbHighPart);
#endif // END KERNEL/USER MODE CHECK
if (cbFileLen < sizeof(MAC_STRUCT))
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
// init the key table
deskey(&DESKeyTable, rgbMACDESKey);
// MAC the file the following way:
// - MAC from start to image check sum
// - skip over the image check sum
// - mac from after the image check sum to the mac struct
// - skip over the mac struct
// - mac the rest of the file
// MAC from the start to the image check sum offset
Status = MACBytesOfFile(
hFile,
cbImageCheckSumOffset,
&DESKeyTable,
rgbTmp,
&cbTmp,
pbMAC,
FALSE,
FALSE
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// Skip over the image checksum
Status = MACBytesOfFile(
hFile,
sizeof(DWORD),
&DESKeyTable,
rgbTmp,
&cbTmp,
pbMAC,
TRUE,
FALSE
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// MAC from after the image checksum to the MAC struct offset
cbBytesToMac = cbMACStructOffset - sizeof(DWORD) - cbImageCheckSumOffset;
Status = MACBytesOfFile(
hFile,
cbBytesToMac,
&DESKeyTable,
rgbTmp,
&cbTmp,
pbMAC,
FALSE,
FALSE
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// skip over the mac struct
Status = MACBytesOfFile(
hFile,
sizeof(MAC_STRUCT),
&DESKeyTable,
rgbTmp,
&cbTmp,
pbMAC,
TRUE,
FALSE
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// MAC data after the MAC struct
cbBytesToMac = cbFileLen - cbMACStructOffset - sizeof(MAC_STRUCT);
Status = MACBytesOfFile(
hFile,
cbBytesToMac,
&DESKeyTable,
rgbTmp,
&cbTmp,
pbMAC,
FALSE,
TRUE
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
Ret:
#ifdef KERNEL_MODE
if (fFileOpened)
{
ZwClose(hFile);
}
#else
if (INVALID_HANDLE_VALUE != hFile)
{
CloseHandle(hFile);
}
#endif
return Status;
}
// **********************************************************************
// SelfMACCheck performs a DES MAC on the binary image of this DLL
// **********************************************************************
NTSTATUS
SelfMACCheck(
IN LPWSTR pszImage
)
{
UCHAR rgbMac[DES_BLOCKLEN];
NTSTATUS Status = STATUS_SUCCESS;
g_pMACStruct = (MAC_STRUCT*) g_pszMAC;
Status = MACTheFile(
pszImage,
g_pMACStruct->dwImageCheckSumOffset,
g_pMACStruct->dwMACStructOffset,
rgbMac
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
if (RtlCompareMemory(
rgbMac,
g_pMACStruct->rgbMac,
sizeof(rgbMac)) != sizeof(rgbMac))
{
Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
goto Ret;
}
Ret:
return Status;
}
#ifndef KERNEL_MODE
//
// Find the offset to the MAC structure
//
NTSTATUS
FindTheMACStructOffset(
LPWSTR pszImage,
ULONG *pcbMACStructOffset
)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
ULONG cbRemaining, cbBytesRead, cbHighPart, cbFileLen, cbCompare = 0;
UCHAR b;
NTSTATUS Status = STATUS_SUCCESS;
*pcbMACStructOffset = 0;
// Load the file
if ((hFile = CreateFileW(
pszImage,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) == INVALID_HANDLE_VALUE)
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
cbFileLen = GetFileSize(hFile, &cbHighPart);
cbRemaining = cbFileLen;
// read file to the correct location
while (cbRemaining > 0)
{
if(!ReadFile(hFile,
&b,
1,
&cbBytesRead,
NULL))
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
if (cbBytesRead != 1)
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
if (b == g_pszMAC[cbCompare])
{
cbCompare++;
if (cbCompare == 8)
{
*pcbMACStructOffset = (cbFileLen - (cbRemaining + 7)) ;
break;
}
}
else
{
cbCompare = 0;
}
cbRemaining--;
}
if (cbCompare != 8)
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
Ret:
if (INVALID_HANDLE_VALUE != hFile)
{
CloseHandle(hFile);
}
return Status;
}
NTSTATUS
GetImageCheckSumOffset(
LPWSTR pszImage,
ULONG *pcbImageCheckSumOffset
)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
HANDLE hFileMap = INVALID_HANDLE_VALUE;
ULONG cbHighPart;
ULONG cbFileLen;
PBYTE pbFilePtr = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PIMAGE_NT_HEADERS pImageNTHdrs;
DWORD OldCheckSum;
DWORD NewCheckSum;
if (INVALID_HANDLE_VALUE == (hFile = CreateFileW(
pszImage,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)))
{
goto Ret;
}
// make sure the file is larger than the indicated offset
cbFileLen = GetFileSize(hFile, &cbHighPart);
// map the file to memory
if (NULL == (hFileMap = CreateFileMapping(
hFile,
NULL,
PAGE_READWRITE,
0,
0,
NULL)))
{
goto Ret;
}
// get a memory view of the file
if (NULL == (pbFilePtr = (PBYTE) MapViewOfFile(
hFileMap,
FILE_MAP_ALL_ACCESS,
0,
0,
0)))
{
goto Ret;
}
// get the pointer to the image checksum
if (NULL == (pImageNTHdrs = CheckSumMappedFile(
pbFilePtr, cbFileLen,
&OldCheckSum, &NewCheckSum))) {
goto Ret;
}
*pcbImageCheckSumOffset =
(ULONG) ((PBYTE) &pImageNTHdrs->OptionalHeader.CheckSum - pbFilePtr);
Status = STATUS_SUCCESS;
Ret:
if (pbFilePtr) {
UnmapViewOfFile(pbFilePtr);
}
if (INVALID_HANDLE_VALUE != hFileMap) {
CloseHandle(hFileMap);
}
if (INVALID_HANDLE_VALUE != hFile)
{
CloseHandle(hFile);
}
return Status;
}
// write the MAC information into the MAC struct in the file
NTSTATUS
WriteMACToTheFile(
LPWSTR pszImage,
MAC_STRUCT *pMacStructOriginal,
ULONG cbMACStructOffset,
UCHAR *pbMac
)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
HANDLE hFileMap = INVALID_HANDLE_VALUE;
PBYTE pbFilePtr = NULL;
MAC_STRUCT TmpMacStruct;
ULONG cbWritten = 0, cbRemaining = cbMACStructOffset;
ULONG cbToRead, cbBytesRead, cbHighPart,cbFileLen;
UCHAR rgbChunk[CSP_TO_BE_MACED_CHUNK];
NTSTATUS Status = STATUS_SUCCESS;
DWORD OldCheckSum, NewCheckSum;
PIMAGE_NT_HEADERS pImageNTHdrs;
RtlCopyMemory(&TmpMacStruct, pMacStructOriginal, sizeof(TmpMacStruct));
// Load the file
if ((hFile = CreateFileW(
pszImage,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) == INVALID_HANDLE_VALUE)
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
// make sure the file is larger than the indicated offset
cbFileLen = GetFileSize(hFile, &cbHighPart);
if (cbFileLen < cbMACStructOffset)
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
// map the file to memory
if ((hFileMap = CreateFileMapping(
hFile,
NULL,
PAGE_READWRITE,
0,
0,
NULL)) == NULL)
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
// get a memory view of the file
if ((pbFilePtr = (PBYTE) MapViewOfFile(
hFileMap,
FILE_MAP_ALL_ACCESS,
0,
0,
0)) == NULL)
{
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
// get the pointer to the image checksum
if (NULL == (pImageNTHdrs = CheckSumMappedFile(
pbFilePtr, cbFileLen,
&OldCheckSum, &NewCheckSum))) {
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
// set up and write the MAC struct
TmpMacStruct.dwImageCheckSumOffset =
(ULONG) ((PBYTE) &pImageNTHdrs->OptionalHeader.CheckSum - pbFilePtr);
TmpMacStruct.dwMACStructOffset = cbMACStructOffset;
RtlCopyMemory(TmpMacStruct.rgbMac, pbMac, sizeof(TmpMacStruct.rgbMac));
// now copy the new mac struct back to the view
RtlCopyMemory(pbFilePtr + cbMACStructOffset, &TmpMacStruct, sizeof(TmpMacStruct));
// compute a new checksum
if (NULL == (pImageNTHdrs = CheckSumMappedFile(
pbFilePtr, cbFileLen,
&OldCheckSum, &NewCheckSum))) {
Status = STATUS_UNSUCCESSFUL;
goto Ret;
}
// and copy the new checksum back to the header
CopyMemory(&pImageNTHdrs->OptionalHeader.CheckSum, &NewCheckSum, sizeof(DWORD));
Ret:
if (pbFilePtr) {
UnmapViewOfFile(pbFilePtr);
}
if (INVALID_HANDLE_VALUE != hFileMap) {
CloseHandle(hFileMap);
}
if (INVALID_HANDLE_VALUE != hFile)
{
CloseHandle(hFile);
}
return Status;
}
// **********************************************************************
// MACTheBinary performs a MAC on the binary and writes the value into
// the g_pMACStruct
// **********************************************************************
NTSTATUS
MACTheBinary(
IN LPWSTR pszImage
)
{
UCHAR rgbMAC[DES_BLOCKLEN];
ULONG cbMACStructOffset = 0, cbImageCheckSumOffset = 0;
NTSTATUS Status = STATUS_SUCCESS;
g_pMACStruct = (MAC_STRUCT*) g_pszMAC;
// Find the offset to the MAC structure
Status = FindTheMACStructOffset(
pszImage,
&cbMACStructOffset
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// Get the offset of the image checksum
Status = GetImageCheckSumOffset(
pszImage,
&cbImageCheckSumOffset
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// MAC the file
Status = MACTheFile(
pszImage,
cbImageCheckSumOffset,
cbMACStructOffset,
rgbMAC
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// write the MAC information into the MAC struct in the file
Status = WriteMACToTheFile(
pszImage,
g_pMACStruct,
cbMACStructOffset,
rgbMAC
);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
Ret:
return Status;
}
#endif // NOT IN KERNEL_MODE