mirror of https://github.com/lianthony/NT4.0
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.
585 lines
17 KiB
585 lines
17 KiB
/*
|
|
* Microsoft Confidential
|
|
* Copyright (C) Microsoft Corporation 1995
|
|
* All Rights Reserved.
|
|
*
|
|
*
|
|
* Author:
|
|
* Jeffrey S Webber
|
|
*
|
|
* History:
|
|
* 5/3/95 Started
|
|
* 5/30/95 Changed pszMSSFFile -> pszFilePath
|
|
* Changed pszNewName -> pszNewFilePath
|
|
* Changed cbNewName -> cbNewFilePath
|
|
* Set MSSFVerify() to _stdcall calling convention
|
|
* #ifdef'd out the encryption step since RSA Lib is broken
|
|
*/
|
|
|
|
|
|
#include "project.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
#include "cpldebug.h"
|
|
#include "sha.h"
|
|
#include <mssfchek.h>
|
|
|
|
|
|
// Lets do our I/O in 4k units
|
|
// Between each I/O we will call up with status
|
|
#define IOUNIT 4096
|
|
|
|
|
|
// The RSA Encryption routines need a buffer
|
|
// this size to support a 1024 bit key
|
|
#define CRYPTCLEAR 136
|
|
|
|
|
|
|
|
// Percentage Complete Steps
|
|
#define INITIALIZED 5
|
|
#define FILE_COMPLETE 85
|
|
#define CRYPTCOMPLETE 90
|
|
#define FILEADJUSTED 95
|
|
#define FINISHED 100
|
|
// Percentage Complete Multiplier
|
|
#define FILEPORTION 80
|
|
|
|
|
|
// Set of Bits to keep track of what needs
|
|
// to be cleaned up on failure
|
|
#define NEEDCLOSE 0x0001
|
|
#define MAPPING_OBJECT 0x0002
|
|
#define MAPPING_VIEW 0x0004
|
|
#define FILENAME 0x0008
|
|
|
|
|
|
|
|
// Public RSA Key to decrypt signature
|
|
extern LPBSAFE_PUB_KEY PubKey;
|
|
|
|
|
|
|
|
/*
|
|
* F i l e E x i s t s ( )
|
|
*
|
|
* Check to see whether specified file exists
|
|
*
|
|
* BUGBUG: Is there a better way to do this?
|
|
*
|
|
* Entry:
|
|
* pszFileName - Name of file to check
|
|
*
|
|
* Exit:
|
|
* TRUE - File Exists
|
|
* FALSE - File does not exist
|
|
*
|
|
*/
|
|
|
|
static
|
|
BOOL
|
|
FileExists( LPCSTR pszFileName )
|
|
{
|
|
HANDLE hFile;
|
|
|
|
ASSERT( pszFileName );
|
|
|
|
hFile = CreateFile( pszFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
return( FALSE );
|
|
|
|
CloseHandle( hFile );
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* C h a n g e F i l e S i z e ( )
|
|
*
|
|
* Truncate file to specified size
|
|
*
|
|
* Entry:
|
|
* pszFileName - Name of file to truncate
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE , error set to Win32 Error that occured
|
|
*/
|
|
|
|
static
|
|
BOOL
|
|
ChangeFileSize( LPCSTR pszFileName, DWORD NewSize, DWORD *error )
|
|
{
|
|
HANDLE hFile;
|
|
|
|
ASSERT( pszFileName );
|
|
|
|
// IF error then it must be Win32 error
|
|
hFile = CreateFile( pszFileName,GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,INVALID_HANDLE_VALUE);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
DEBUGMSG("ChangeFileSize(): CreateFile()");
|
|
*error = GetLastError();
|
|
return( FALSE );
|
|
}
|
|
|
|
if (SetFilePointer( hFile, NewSize, NULL, FILE_BEGIN) == (DWORD) -1) {
|
|
DEBUGMSG("ChangeFileSize(): SetFilePointer()");
|
|
*error = GetLastError();
|
|
CloseHandle( hFile );
|
|
return( FALSE );
|
|
}
|
|
|
|
if (SetEndOfFile( hFile ) != TRUE) {
|
|
DEBUGMSG("ChangeFileSize(): SetEndOfFile()");
|
|
*error = GetLastError();
|
|
CloseHandle( hFile );
|
|
return( FALSE );
|
|
}
|
|
|
|
CloseHandle( hFile );
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* M a k e N e w F i l e N a m e ( )
|
|
*
|
|
* Take the old name of the file and replace just the file
|
|
* portion keeping the old path...
|
|
*
|
|
* Entry:
|
|
* pszSrcPath - Old name including full path
|
|
* pszNewFilename - New name portion of new filename
|
|
* pszDestPath - Buffer to contain newly assembled name
|
|
* cbDestPath - size of buffer pointed to buy pszDestPath
|
|
*
|
|
* Exit-Success:
|
|
* Returns True - pszDestPath filled in
|
|
*
|
|
* Exit-Failure:
|
|
* Returns False - only reason would be if buffer wasn't big enough
|
|
*/
|
|
static
|
|
BOOL
|
|
MakeNewName( LPCSTR pszSrcPath, LPCSTR pszNewFilename, LPSTR pszDestPath, UINT cbDestPath)
|
|
{
|
|
LPCSTR p; // String Rover
|
|
LPCSTR pchLastSep; // Will point to last path separator in pszSrcPath
|
|
|
|
|
|
ASSERT( pszSrcPath );
|
|
ASSERT( pszNewFilename );
|
|
ASSERT( pszDestPath );
|
|
|
|
pchLastSep = pszSrcPath;
|
|
p = pszSrcPath;
|
|
while (*p != '\0') {
|
|
switch (*p) {
|
|
case '\\':
|
|
case ':':
|
|
// Point at char after separator, character or '\0'
|
|
pchLastSep = p + 1;
|
|
break;
|
|
}
|
|
p = CharNext(p);
|
|
}
|
|
|
|
//
|
|
// Make sure that final file name fits into supplied buffer by
|
|
// calculating:
|
|
// Original Full Path and File Name
|
|
// - Just Filename part
|
|
// + New Filename part
|
|
// + Space for terminator
|
|
// = Space to hold resulting new name
|
|
//
|
|
if ((UINT) (lstrlen( pszSrcPath ) - lstrlen(pchLastSep) + lstrlen(pszNewFilename) + 1) > cbDestPath)
|
|
return( FALSE );
|
|
|
|
// Copy pszSrcPath up to and including last separator
|
|
lstrcpyn( pszDestPath, pszSrcPath, pchLastSep - pszSrcPath + 1);
|
|
// Terminate it
|
|
pszDestPath[pchLastSep - pszSrcPath] = '\0';
|
|
// Cat the new name onto it
|
|
lstrcat( pszDestPath, pszNewFilename );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* M S S F V e r i f y ( )
|
|
*
|
|
* Verify a MSSF File
|
|
*
|
|
*/
|
|
|
|
BOOL _stdcall
|
|
MSSFVerify( PMSSFVFY pVerify)
|
|
{
|
|
HANDLE hFile; // Handle to MSSF File
|
|
HANDLE hMapFile; // Handle to Mapped File Object
|
|
MSSUFFIX suffix;
|
|
UCHAR *pFile; // Pointer to MemMapped File
|
|
DWORD fNeedClean = 0; // Keep Track of resources for cleanup
|
|
int offset, cbFSize, LastDataByte;
|
|
int cbRemain;
|
|
A_SHA_CTX SHA_Hash;
|
|
UCHAR SHA_Digest[A_SHA_DIGEST_LEN];
|
|
LPSTR pszNameInFile;
|
|
UCHAR CryptSig[CRYPTCLEAR];
|
|
UCHAR PlainSig[CRYPTCLEAR];
|
|
UCHAR GenSig[CRYPTCLEAR];
|
|
DWORD dwCBRet;
|
|
NAMECHECK NameCheck;
|
|
BOOL fReplace = FALSE;
|
|
static const char szMapName[] = "MappedMSSF";
|
|
|
|
|
|
// Sanity check
|
|
if (pVerify == NULL) {
|
|
DEBUGMSG("pVerify is NULL!");
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
|
|
// Error Handling Note:
|
|
// In order to reduce repeated code to set error code
|
|
// and type we assume that the errortype is Win32 and
|
|
// that the error is ERROR_SUCCESS
|
|
//
|
|
// If the Error or ErrorType haven't changed and we
|
|
// reach the cleanup section then we know that we've
|
|
// encountered a Win32 error so we do a GetLastError()
|
|
// before cleaning.
|
|
//
|
|
// All this to make the code a bit smaller
|
|
|
|
pVerify->ErrorType = ERRTYPE_WIN;
|
|
pVerify->Error = ERROR_SUCCESS;
|
|
|
|
// Check to make sure filename isn't NULL
|
|
if (pVerify->pszFilePath == NULL) {
|
|
DEBUGMSG("Pointer to filename to check is NULL");
|
|
pVerify->Error = ERROR_INVALID_PARAMETER;
|
|
goto failure;
|
|
}
|
|
|
|
// Open Specified File
|
|
hFile = CreateFile( pVerify->pszFilePath,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,INVALID_HANDLE_VALUE);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
DEBUGMSG("Initial CreateFile() Failed");
|
|
goto failure;
|
|
}
|
|
fNeedClean |= NEEDCLOSE;
|
|
|
|
|
|
// Get File Size
|
|
cbFSize = GetFileSize( hFile, NULL );
|
|
if (cbFSize == (DWORD) -1) {
|
|
DEBUGMSG("GetFileSize() Failed\n");
|
|
goto failure;
|
|
}
|
|
|
|
|
|
// The file better be at least as long as a suffix
|
|
if (cbFSize < sizeof(suffix)) {
|
|
DEBUGMSG("File too small");
|
|
pVerify->Error = ERROR_NOT_MSSF;
|
|
pVerify->ErrorType = ERRTYPE_MSSF;
|
|
goto failure;
|
|
}
|
|
|
|
// Memory Map the file!!
|
|
hMapFile = CreateFileMapping( hFile,NULL,PAGE_READONLY,0,0, szMapName );
|
|
if (hMapFile == NULL) {
|
|
DEBUGMSG("CreateFileMapping() Failure");
|
|
goto failure;
|
|
}
|
|
fNeedClean |= MAPPING_OBJECT;
|
|
pFile = (UCHAR *) MapViewOfFile( hMapFile, FILE_MAP_READ,0,0,0 );
|
|
if (pFile == NULL) {
|
|
DEBUGMSG("MapViewOfFile() Failure");
|
|
goto failure;
|
|
}
|
|
fNeedClean |= MAPPING_VIEW;
|
|
|
|
|
|
|
|
// Calculate offset into mapped file of suffix
|
|
offset = cbFSize - sizeof( suffix );
|
|
|
|
// Copy Suffix into properly aligned structure on stack
|
|
// since it might not be properly aligned in file mapping
|
|
memcpy( &suffix, &(pFile[offset]), sizeof( suffix ));
|
|
|
|
// Sanity check on suffix
|
|
if (suffix.Magic != MSSF_MAGIC) {
|
|
pVerify->Error = ERROR_NOT_MSSF;
|
|
pVerify->ErrorType = ERRTYPE_MSSF;
|
|
goto failure;
|
|
}
|
|
|
|
|
|
// Copy File Name from suffix area to allocated buffer if necessary
|
|
if (suffix.cbName > 0) {
|
|
if (suffix.cbName > pVerify->cbNewFilePath) {
|
|
DEBUGMSG("Filename in file is larger than supplied buffer");
|
|
pVerify->Error = ERROR_INSUFFICIENT_BUFFER;
|
|
goto failure;
|
|
}
|
|
offset -= suffix.cbName;
|
|
|
|
pszNameInFile = (LPSTR) malloc( suffix.cbName );
|
|
if (pszNameInFile == NULL) {
|
|
DEBUGMSG("Memory Allocation for filename from file failed");
|
|
pVerify->Error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto failure;
|
|
}
|
|
fNeedClean |= FILENAME;
|
|
|
|
memcpy( pszNameInFile, &(pFile[offset]), suffix.cbName );
|
|
}
|
|
|
|
|
|
// Status Report
|
|
// Lets just call this 5% complete
|
|
if (pVerify->pfnCallback) {
|
|
if ((*pVerify->pfnCallback)(CALLBACK_STATUS, (void *) INITIALIZED, pVerify->lpUserData ) == 0) {
|
|
pVerify->Error = ERROR_CB_CANCEL;
|
|
pVerify->ErrorType = ERRTYPE_MSSF;
|
|
goto failure;
|
|
}
|
|
}
|
|
|
|
// Initialize SHA Hash
|
|
A_SHAInit( &SHA_Hash );
|
|
|
|
// Rememember location of last data by of file
|
|
LastDataByte = offset;
|
|
|
|
// Seek to beginning of file
|
|
offset = 0;
|
|
|
|
|
|
// Hash File Data
|
|
while (offset + IOUNIT < LastDataByte) {
|
|
if (pVerify->pfnCallback) {
|
|
// Update Caller's idea of status
|
|
if ((*pVerify->pfnCallback)(CALLBACK_STATUS, (void *) (INITIALIZED + ((FILEPORTION * offset)/LastDataByte)), pVerify->lpUserData ) == 0) {
|
|
pVerify->Error = ERROR_CB_CANCEL;
|
|
pVerify->ErrorType = ERRTYPE_MSSF;
|
|
goto failure;
|
|
}
|
|
}
|
|
|
|
A_SHAUpdate( &SHA_Hash, &(pFile[offset]), IOUNIT );
|
|
offset += IOUNIT;
|
|
}
|
|
if (offset < LastDataByte) {
|
|
cbRemain = LastDataByte - offset;
|
|
A_SHAUpdate( &SHA_Hash, &(pFile[offset]), cbRemain );
|
|
}
|
|
|
|
|
|
// Done all file data checking
|
|
// Update Caller's idea of status
|
|
if (pVerify->pfnCallback) {
|
|
if ((*pVerify->pfnCallback)(CALLBACK_STATUS, (void *) FILE_COMPLETE, pVerify->lpUserData) == 0) {
|
|
pVerify->Error = ERROR_CB_CANCEL;
|
|
pVerify->ErrorType = ERRTYPE_MSSF;
|
|
goto failure;
|
|
}
|
|
}
|
|
|
|
// Hash everything except signature in exact same
|
|
// way as the stuff is hashed in the signing process
|
|
A_SHAUpdate( &SHA_Hash, (UCHAR *) &(suffix.cbName), sizeof(DWORD) );
|
|
if (suffix.cbName > 0)
|
|
A_SHAUpdate( &SHA_Hash, (UCHAR *) pszNameInFile, lstrlen( pszNameInFile ) );
|
|
A_SHAUpdate( &SHA_Hash, (UCHAR *) &(suffix.cbSize), sizeof(DWORD) );
|
|
A_SHAUpdate( &SHA_Hash, (UCHAR *) &(suffix.Version), sizeof(DWORD) );
|
|
A_SHAUpdate( &SHA_Hash, (UCHAR *) &(suffix.Magic), sizeof(DWORD) );
|
|
|
|
// Finalize the Message Digest
|
|
A_SHAFinal( &SHA_Hash, SHA_Digest );
|
|
|
|
|
|
// Decrypt the digest
|
|
memset( CryptSig, 0, sizeof( CryptSig ) );
|
|
memset( PlainSig, 0, sizeof( PlainSig ) );
|
|
|
|
memcpy( CryptSig, suffix.DigitalSig, PubKey->datalen + 1);
|
|
if (! BSafeEncPublic( PubKey, CryptSig, PlainSig )) {
|
|
pVerify->Error = ERROR_DECRYPTION;
|
|
pVerify->ErrorType = ERRTYPE_MSSF;
|
|
goto failure;
|
|
}
|
|
|
|
// Done Decrypting
|
|
if (pVerify->pfnCallback) {
|
|
if ((*pVerify->pfnCallback)(CALLBACK_STATUS, (void *) CRYPTCOMPLETE, pVerify->lpUserData) == 0) {
|
|
pVerify->Error = ERROR_CB_CANCEL;
|
|
pVerify->ErrorType = ERRTYPE_MSSF;
|
|
goto failure;
|
|
}
|
|
}
|
|
|
|
memset( GenSig, 0, sizeof(GenSig) );
|
|
memset( GenSig, 0xff, PubKey->datalen - 1);
|
|
memcpy( GenSig, SHA_Digest, A_SHA_DIGEST_LEN );
|
|
|
|
|
|
// Check Signature
|
|
if (memcmp( PlainSig, GenSig, PubKey->datalen + 1) != 0) {
|
|
pVerify->Error = ERROR_BAD_SIG;
|
|
pVerify->ErrorType = ERRTYPE_MSSF;
|
|
goto failure;
|
|
}
|
|
|
|
|
|
// The Signature is VERIFIED!!
|
|
// Now we begin cleanup and file adjustment as necessary
|
|
|
|
|
|
|
|
// Close Up Mem Map File
|
|
if (! UnmapViewOfFile(pFile)) {
|
|
goto failure;
|
|
}
|
|
fNeedClean &= ~MAPPING_VIEW;
|
|
|
|
if (! CloseHandle( hMapFile )) {
|
|
goto failure;
|
|
}
|
|
fNeedClean &= ~MAPPING_OBJECT;
|
|
|
|
if (! CloseHandle( hFile )) {
|
|
goto failure;
|
|
}
|
|
fNeedClean &= ~NEEDCLOSE;
|
|
|
|
|
|
// Rename/Truncate as required
|
|
if ((pVerify->dwVerifyFlags == MSSF_DEFAULT) && (suffix.cbName > 0)) {
|
|
if (MakeNewName( pVerify->pszFilePath, pszNameInFile, pVerify->pszNewFilePath, pVerify->cbNewFilePath ) != TRUE) {
|
|
DEBUGMSG("Provided destination buffer not large enough");
|
|
pVerify->Error = ERROR_INSUFFICIENT_BUFFER;
|
|
goto failure;
|
|
}
|
|
|
|
|
|
//
|
|
// If the destination file already exists then we have to
|
|
// check back for permission to replace it.
|
|
//
|
|
// No callback function specified or a return of 0 means that
|
|
// we won't replace it, and instead return ERROR_FILE_EXISTS
|
|
|
|
if (FileExists( pVerify->pszNewFilePath )) {
|
|
if (pVerify->pfnCallback == NULL) {
|
|
pVerify->Error = ERROR_FILE_EXISTS;
|
|
goto failure;
|
|
}
|
|
|
|
// Initialize Callback Name Check/Change structure
|
|
NameCheck.pszFileName = pVerify->pszNewFilePath;
|
|
NameCheck.cbFileNameBuffer = pVerify->cbNewFilePath;
|
|
while (FileExists( pVerify->pszNewFilePath )) {
|
|
// Call up and find out what user/program wants
|
|
dwCBRet = (*pVerify->pfnCallback)(CALLBACK_DESTEXISTS, (void *) &NameCheck, pVerify->lpUserData );
|
|
switch (dwCBRet) {
|
|
case CB_RET_DONT_REPLACE: // Clean Up and Return Error
|
|
pVerify->Error = ERROR_FILE_EXISTS;
|
|
goto failure;
|
|
case CB_RET_CHECK_AGAIN: // Hopefully name has been changed
|
|
continue;
|
|
case CB_RET_REPLACE:
|
|
fReplace = TRUE;
|
|
break; // Fall out and delete file
|
|
default:
|
|
pVerify->Error = ERROR_FILE_EXISTS;
|
|
goto failure;
|
|
}
|
|
if (fReplace)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (FileExists( pVerify->pszNewFilePath )) {
|
|
// Delete the file
|
|
if (DeleteFile( pVerify->pszNewFilePath) == FALSE) {
|
|
goto failure;
|
|
}
|
|
}
|
|
|
|
// Rename File
|
|
if (MoveFile( pVerify->pszFilePath, pVerify->pszNewFilePath) != TRUE) {
|
|
DEBUGMSG("MoveFile() Failed");
|
|
goto failure;
|
|
}
|
|
|
|
// Truncate
|
|
if (ChangeFileSize( pVerify->pszNewFilePath, LastDataByte, &(pVerify->Error)) != TRUE) {
|
|
goto failure;
|
|
}
|
|
|
|
// Don't need name from file
|
|
free( pszNameInFile );
|
|
fNeedClean &= ~FILENAME;
|
|
} else {
|
|
if ((UINT) lstrlen(pVerify->pszFilePath) + 1 > pVerify->cbNewFilePath ) {
|
|
DEBUGMSG("Newname == Oldname but buffer isn't big enough");
|
|
pVerify->Error = ERROR_INSUFFICIENT_BUFFER;
|
|
goto failure;
|
|
}
|
|
lstrcpy( pVerify->pszNewFilePath, pVerify->pszFilePath );
|
|
}
|
|
|
|
|
|
// Hey We're 100% complete!
|
|
if (pVerify->pfnCallback) {
|
|
if ((*pVerify->pfnCallback)(CALLBACK_STATUS, (void *) FINISHED, pVerify->lpUserData) == 0) {
|
|
pVerify->Error = ERROR_CB_CANCEL;
|
|
pVerify->ErrorType = ERRTYPE_MSSF;
|
|
goto failure;
|
|
}
|
|
}
|
|
return( TRUE );
|
|
|
|
failure:
|
|
|
|
// Get the error if it was a Win32 problem
|
|
if ((pVerify->ErrorType == ERRTYPE_WIN) && (pVerify->Error == ERROR_SUCCESS))
|
|
pVerify->Error = GetLastError();
|
|
|
|
// Clean up
|
|
if (fNeedClean & MAPPING_VIEW)
|
|
UnmapViewOfFile(pFile);
|
|
|
|
if (fNeedClean & MAPPING_OBJECT)
|
|
CloseHandle( hMapFile );
|
|
|
|
if (fNeedClean & NEEDCLOSE)
|
|
CloseHandle( hFile );
|
|
|
|
if (fNeedClean & FILENAME)
|
|
free( pszNameInFile );
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
|
|
|