|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows NT Security
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: updcat.cpp
//
// Contents: Update Catalog Entry
//
// History: 02-Sep-98 kirtd Created
//
//----------------------------------------------------------------------------
#include <windows.h>
#include <assert.h>
#include "wincrypt.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <time.h>
#include <mscat.h>
#include <mssip.h>
#include <sipguids.h>
#include <wintrust.h>
// Prototypes
// BOOL AddFileToCatalog (IN HANDLE hCatalog, IN LPWSTR pwszFileName);
BOOL RemoveHashFromCatalog(IN LPWSTR pwszCatalogFile, IN LPSTR pszHash); BOOL AddFileOrAuthAttrToCatalog(IN HANDLE hCatalog, IN LPWSTR pwszFileName, IN DWORD dwAttrFlags, IN LPWSTR pwszAttrName, IN LPWSTR pwszAttrValue); BOOL CheckFileSize(IN LPWSTR pFileName, IN ULONG size); extern "C" BOOL MsCatConstructHashTag (IN DWORD cbDigest, IN LPBYTE pbDigest, OUT LPWSTR* ppwszHashTag); extern "C" VOID MsCatFreeHashTag (IN LPWSTR pwszHashTag);
#define AddFileToCatalog(cat, file) AddFileOrAuthAttrToCatalog(cat, file, 0, NULL, NULL)
#define PWSZ_SPATTR L"SpAttr"
#define PSZ_SPATTR_OPTION "-SpAttr:"
ULONG sizeLimit = 0;
//+---------------------------------------------------------------------------
//
// Function: Usage
//
// Synopsis: prints the usage statement
//
//----------------------------------------------------------------------------
static void Usage(void) { printf("Usage: updcat <Catalog File> [-s <Size>] [-a <FileName>]\n"); printf("Usage: updcat <Catalog File> [-d <Hash>]\n"); printf("Usage: updcat <Catalog File> [-s <Size>] [-r <Hash> <FileName>]\n"); printf("Usage: updcat <Catalog File> [-attr <FileName> <Name> <Value>]\n"); printf(" -a, add the file by hash to the catalog\n"); printf(" -d, delete the hash from the catalog\n"); printf(" -r, replace the hash in the catalog with the hash of the file\n"); printf(" -s, fail if the file is smaller than this size (in bytes)\n"); printf(" -attr, add an ASCII attribute to a file's catalog entry\n"); printf(" -SpAttr:, replace the current (or add new) SpAttr in the catalog\n"); }
//+---------------------------------------------------------------------------
//
// Function: main
//
// Synopsis: main program entry point
//
//----------------------------------------------------------------------------
int _cdecl main(int argc, char * argv[]) { BOOL fResult = TRUE; LPSTR pszCatalogFile = NULL; LPWSTR pwszCatalogFile = NULL; LPSTR pszFileName = NULL; LPSTR pszHash = NULL; LPWSTR pwszFileName = NULL; LPSTR pszAttrName = NULL; LPWSTR pwszAttrName = NULL; LPSTR pszAttrValue = NULL; LPWSTR pwszAttrValue = NULL; BOOL fAddEntry = FALSE; DWORD cch = 0; HANDLE hCatalog = NULL; BOOL fOptionChosen = FALSE; LPWSTR pwszSpAttr = NULL; CRYPTCATATTRIBUTE *pCatAttr = NULL;
if ( argc < 2 ) { Usage(); return( 1 ); }
argv++; argc--;
printf( "command line: %s\n", GetCommandLineA() );
pszCatalogFile = argv[0]; cch = strlen( pszCatalogFile );
while ( --argc > 0 ) { if ( **++argv == '-' ) { switch( argv[0][1] ) { case 'a': case 'A':
if ( argc < 2 ) { Usage(); return( 1 ); }
pszFileName = argv[1]; fAddEntry = TRUE;
if (_strcmpi(&argv[0][1], "attr") == 0) { if ( argc < 4 ) { Usage(); return( 1 ); }
pszAttrName = argv[2]; pszAttrValue = argv[3]; } break;
case 'd': case 'D':
if ( argc < 2 ) { Usage(); return( 1 ); }
pszHash = argv[1]; break;
case 'r': case 'R':
if ( argc < 3 ) { Usage(); return( 1 ); }
pszHash = argv[1]; fAddEntry = TRUE; pszFileName = argv[2]; break;
case 'S':
if (memcmp(&(argv[0][0]), (void *) PSZ_SPATTR_OPTION, strlen(PSZ_SPATTR_OPTION)) == 0) { DWORD dwNumChars;
dwNumChars = MultiByteToWideChar( CP_ACP, 0, &(argv[0][strlen(PSZ_SPATTR_OPTION)]), -1, NULL, 0 );
if (dwNumChars == 0) { printf( "Error calling MultiByteToWideChar on %s\n", &(argv[0][strlen(PSZ_SPATTR_OPTION)])); return -1; }
pwszSpAttr = new WCHAR [ dwNumChars ];
if (pwszSpAttr == NULL) { printf("Out of memory error\n"); return -1; }
if ( MultiByteToWideChar( CP_ACP, 0, &(argv[0][strlen(PSZ_SPATTR_OPTION)]), -1, pwszSpAttr, dwNumChars ) == 0 ) {
delete pwszSpAttr; printf( "Error calling MultiByteToWideChar on %s\n", &(argv[0][strlen(PSZ_SPATTR_OPTION)])); return -1; } } else { Usage(); return -1; } break;
case 's': if ((argc < 2) || (argv[0][2] != '\0')) { Usage(); return -1; } sizeLimit = strtol(*(argv+1), NULL, 10); if (errno || (sizeLimit == 0)) { printf ("Invalid size specified with -s option: %s\n",*(argv+1)); Usage(); return -1; } break;
default: Usage(); return -1; }
fOptionChosen = TRUE; argc -= 1; argv++; } } pwszCatalogFile = new WCHAR [ cch + 1 ]; if ( pwszCatalogFile != NULL ) { if ( MultiByteToWideChar( CP_ACP, 0, pszCatalogFile, -1, pwszCatalogFile, cch + 1 ) == 0 ) { delete pwszCatalogFile; return( 1 ); } }
if (!fOptionChosen) { Usage(); delete pwszCatalogFile; return -1; }
if (pszFileName != NULL) { cch = strlen( pszFileName );
pwszFileName = new WCHAR [ cch + 1 ]; if ( pwszFileName != NULL ) { if ( MultiByteToWideChar( CP_ACP, 0, pszFileName, -1, pwszFileName, cch + 1 ) == 0 ) { delete pwszCatalogFile; delete pwszFileName; return( 1 ); } } }
if (pszAttrName != NULL) { cch = strlen( pszAttrName );
pwszAttrName = new WCHAR [ cch + 1 ]; if ( pwszAttrName != NULL ) { if ( MultiByteToWideChar( CP_ACP, 0, pszAttrName, -1, pwszAttrName, cch + 1 ) == 0 ) { printf("Error converting AttrName to wchar\n"); delete pwszCatalogFile; delete pwszFileName; delete pwszAttrName; return( 1 ); } } }
if (pszAttrValue != NULL) { cch = strlen( pszAttrValue );
pwszAttrValue = new WCHAR [ cch + 1 ]; if ( pwszAttrValue != NULL ) { if ( MultiByteToWideChar( CP_ACP, 0, pszAttrValue, -1, pwszAttrValue, cch + 1 ) == 0 ) { printf("Error converting AttrValue to wchar\n"); delete pwszCatalogFile; delete pwszFileName; delete pwszAttrName; delete pwszAttrValue; return( 1 ); } } }
if ( pszHash != NULL ) { fResult = RemoveHashFromCatalog(pwszCatalogFile, pszHash);
if ( fResult == FALSE ) { printf("Error removing <%s> from catalog <%s>\n", pszHash, pszCatalogFile); } }
//
// If there haven't been any errors, and we are adding a hash
//
if (( fResult == TRUE ) && ( fAddEntry == TRUE )) { hCatalog = CryptCATOpen( pwszCatalogFile, CRYPTCAT_OPEN_ALWAYS, NULL, 0x00000001, 0x00010001 );
if ( hCatalog == NULL ) { fResult = FALSE; } else { // If we're adding an attribute
if (pwszAttrName && pwszAttrValue) { fResult = AddFileOrAuthAttrToCatalog( hCatalog, pwszFileName, 0x10010001, pwszAttrName, pwszAttrValue ); CryptCATClose( hCatalog );
if ( fResult == FALSE ) { printf("Error adding Attribute <%s> to catalog <%s>\n", pszAttrName, pszCatalogFile); } } else // If we're only adding the file by hash
{ fResult = AddFileToCatalog( hCatalog, pwszFileName ); CryptCATClose( hCatalog );
if ( fResult == FALSE ) { printf("Error adding <%s> to catalog <%s>\n", pszFileName, pszCatalogFile); } } }
}
if ( pwszSpAttr != NULL ) { hCatalog = CryptCATOpen( pwszCatalogFile, CRYPTCAT_OPEN_ALWAYS, NULL, 0x00000001, 0x00010001 );
if ( hCatalog == NULL ) { fResult = FALSE; goto Return; }
//
// Check to see if it already has an SpAttr
//
pCatAttr = CryptCATGetCatAttrInfo(hCatalog, PWSZ_SPATTR);
if ( pCatAttr == NULL ) { if (NULL == CryptCATPutCatAttrInfo( hCatalog, PWSZ_SPATTR, 0x10010001, (wcslen(pwszSpAttr) + 1) * sizeof(WCHAR), (BYTE *) pwszSpAttr)) { printf("Error adding SpAttr to catalog <%s>\n", pszCatalogFile); fResult = FALSE; goto Return; } } else { if (NULL == CryptCATPutCatAttrInfo( hCatalog, PWSZ_SPATTR, 0x10040001, (wcslen(pwszSpAttr) + 1) * sizeof(WCHAR), (BYTE *) pwszSpAttr)) { if (GetLastError() == ERROR_INVALID_PARAMETER) { printf("The SpAttr modification failed, it is likely due to an old wintrust.dll\n"); } else { printf("Error changing SpAttr in catalog <%s>\n", pszCatalogFile); } fResult = FALSE; goto Return; }
}
CryptCATPersistStore(hCatalog); CryptCATClose( hCatalog ); }
Return: return( !fResult ); }
typedef BOOL (WINAPI *PFN_CRYPTSIP_RETRIEVE_SUBJECT_GUID_FOR_CATALOG_FILE) ( IN LPCWSTR FileName, IN HANDLE hFileIn, OUT GUID *pgSubject );
//+---------------------------------------------------------------------------
//
// Function: AddFileOrAuthAttrToCatalog
//
// Synopsis: add a file as an entry to the catalog. The tag will be the
// hash
// additionally, you can add an authenticated attribute.
//
//----------------------------------------------------------------------------
BOOL AddFileOrAuthAttrToCatalog (IN HANDLE hCatalog, IN LPWSTR pwszFileName, IN DWORD dwAttrFlags, IN LPWSTR pwszAttrName, IN LPWSTR pwszAttrValue) { BOOL fResult; GUID FlatSubject = CRYPT_SUBJTYPE_FLAT_IMAGE; GUID SubjectType; SIP_SUBJECTINFO SubjectInfo; SIP_DISPATCH_INFO DispatchInfo; DWORD cbIndirectData; SIP_INDIRECT_DATA* pIndirectData = NULL; CRYPTCATSTORE* pCatStore = CryptCATStoreFromHandle( hCatalog ); CRYPTCATMEMBER* pMember; CRYPTCATATTRIBUTE* pAttr; LPWSTR pwszHashTag = NULL; HMODULE hMod = NULL; PFN_CRYPTSIP_RETRIEVE_SUBJECT_GUID_FOR_CATALOG_FILE pSIPFunc = NULL;
memset( &SubjectInfo, 0, sizeof( SubjectInfo ) ); memset( &DispatchInfo, 0, sizeof( DispatchInfo ) );
if (sizeLimit) { // Check that we do not add the hash for a file whose
// size if less than the specified lower limit
if ( !CheckFileSize(pwszFileName, sizeLimit) ) { printf ("Error: %S is smaller than the specified minimum size (%d)\n", pwszFileName, sizeLimit); return FALSE; } }
//
// NOTE!!!!!
//
// Try to use the function that only retrieves SIPs for hashing files
// that are to be included in catalog files. This function is new and
// only exists post win2k, so if it isn't there, then fall back to the
// win2k function... which should be OK since Win2k didn't SHIP
// with any SIPs that caused problems (although SIPs could be installed on
// a win2k system after the fact that do cause problems)
//
if (NULL != (hMod = LoadLibrary("crypt32.dll"))) { pSIPFunc = (PFN_CRYPTSIP_RETRIEVE_SUBJECT_GUID_FOR_CATALOG_FILE) GetProcAddress(hMod, "CryptSIPRetrieveSubjectGuidForCatalogFile");
if (pSIPFunc != NULL) { if ( pSIPFunc(pwszFileName, NULL, &SubjectType) == FALSE ) { memcpy( &SubjectType, &FlatSubject, sizeof( GUID ) ); } } }
if (pSIPFunc == NULL) { //
// Fall back to old SIP resolver
//
if ( CryptSIPRetrieveSubjectGuid( pwszFileName, NULL, &SubjectType ) == FALSE ) { memcpy( &SubjectType, &FlatSubject, sizeof( GUID ) ); } }
if (hMod != NULL) { FreeLibrary(hMod); }
if ( CryptSIPLoad( &SubjectType, 0, &DispatchInfo ) == FALSE ) { return( FALSE ); }
// Some of this subject info stuff should be configurable but
// since the CDF API does not allow it, we won't worry about it
// yet.
SubjectInfo.cbSize = sizeof( SubjectInfo ); SubjectInfo.hProv = pCatStore->hProv; SubjectInfo.DigestAlgorithm.pszObjId = (char *)CertAlgIdToOID( CALG_SHA1 );
SubjectInfo.dwFlags = SPC_INC_PE_RESOURCES_FLAG | SPC_INC_PE_IMPORT_ADDR_TABLE_FLAG | MSSIP_FLAGS_PROHIBIT_RESIZE_ON_CREATE;
SubjectInfo.dwEncodingType = pCatStore->dwEncodingType; SubjectInfo.pgSubjectType = &SubjectType; SubjectInfo.pwsFileName = pwszFileName;
fResult = DispatchInfo.pfCreate( &SubjectInfo, &cbIndirectData, NULL );
if ( fResult == TRUE ) { pIndirectData = (SIP_INDIRECT_DATA *)new BYTE [ cbIndirectData ]; if ( pIndirectData != NULL ) { fResult = DispatchInfo.pfCreate( &SubjectInfo, &cbIndirectData, pIndirectData ); } else { SetLastError( E_OUTOFMEMORY ); fResult = FALSE; } }
if ( fResult == TRUE ) { fResult = MsCatConstructHashTag( pIndirectData->Digest.cbData, pIndirectData->Digest.pbData, &pwszHashTag ); }
if ( fResult == FALSE ) { goto Return; }
if (pwszAttrName && pwszAttrValue) { // We're adding an Attribute
if (dwAttrFlags != 0x10010001) { printf("Error: Unsupported flag specified\n"); fResult = FALSE; goto Return; }
// Find the member in the catalog.
pMember = CryptCATGetMemberInfo(hCatalog, pwszHashTag); if (pMember == NULL) { // Catalog member was not found. Adding it...
pMember = CryptCATPutMemberInfo( hCatalog, pwszFileName, pwszHashTag, &SubjectType, SubjectInfo.dwIntVersion, cbIndirectData, (LPBYTE)pIndirectData ); } if (pMember == NULL) { printf("Error: Could not find file hash, and could not add it.\n"); fResult = FALSE; } else { if (pAttr = CryptCATGetAttrInfo(hCatalog, pMember, pwszAttrName)) { if (wcscmp(pwszAttrValue, LPCWSTR(pAttr->pbValue)) == 0) { printf("Attribute already exists with the same value\n"); fResult = FALSE; } else { pAttr->cbValue = (wcslen(pwszAttrValue) + 1) * sizeof(WCHAR); delete(pAttr->pbValue); pAttr->pbValue = (BYTE *)pwszAttrValue; pAttr->pbValue = new BYTE[pAttr->cbValue]; if (pAttr->pbValue) { memcpy(pAttr->pbValue, pwszAttrValue, pAttr->cbValue); fResult = CryptCATPersistStore(hCatalog); } else { pAttr->cbValue = 0; fResult = FALSE; } } } else { pAttr = CryptCATPutAttrInfo(hCatalog, pMember, pwszAttrName, dwAttrFlags, (wcslen(pwszAttrValue) + 1) * sizeof(WCHAR), (BYTE *)pwszAttrValue); if (pAttr != NULL) { fResult = CryptCATPersistStore(hCatalog); } else { fResult = FALSE; } } } } else { // We're just adding a catalog member
// Does this member already exist?
pMember = CryptCATGetMemberInfo(hCatalog, pwszHashTag); if (pMember == NULL) { // it does not exist in the catalog yet. Add it.
pMember = CryptCATPutMemberInfo( hCatalog, pwszFileName, pwszHashTag, &SubjectType, SubjectInfo.dwIntVersion, cbIndirectData, (LPBYTE)pIndirectData );
if ( pMember != NULL ) { fResult = CryptCATPersistStore( hCatalog ); } else { fResult = FALSE; } } else { // It already exists in the catalog.
printf("This file's hash is already present in the catalog.\n"); fResult = FALSE; } }
Return: if ( pwszHashTag != NULL ) { MsCatFreeHashTag( pwszHashTag ); }
delete (LPBYTE)pIndirectData;
return( fResult ); }
//+---------------------------------------------------------------------------
//
// Function: RemoveHashFromCatalog
//
// Synopsis: removes a hash entry from the catalog.
//
//----------------------------------------------------------------------------
BOOL RemoveHashFromCatalog(IN LPWSTR pwszCatalogFile, IN LPSTR pszHash) { BOOL fRet = TRUE; LPSTR pChar = NULL; int i, j; DWORD dwContentType; PCTL_CONTEXT pCTLContext = NULL; CTL_CONTEXT CTLContext; CTL_INFO CTLInfo; DWORD cbEncodedCTL = 0; BYTE *pbEncodedCTL = NULL; DWORD cbWritten = 0; HANDLE hFile = INVALID_HANDLE_VALUE; DWORD cch = 0; LPWSTR pwszHash = NULL; BOOL fHashFound = FALSE;
CMSG_SIGNED_ENCODE_INFO signedInfo; memset(&signedInfo, 0, sizeof(signedInfo)); signedInfo.cbSize = sizeof(signedInfo);
CTLInfo.rgCTLEntry = NULL;
cch = strlen( pszHash );
pwszHash = new WCHAR [ cch + 1 ]; if ( pwszHash == NULL ) { goto ErrorReturn; } if ( MultiByteToWideChar( CP_ACP, 0, pszHash, -1, pwszHash, cch + 1 ) == 0 ) { goto ErrorReturn; }
//
// Get rid of all the ' ' chars
//
i = 0; j = 0; for (i=0; i<(int)wcslen(pwszHash); i++) { if (pwszHash[i] != ' ') { pwszHash[j++] = pwszHash[i]; } } pwszHash[j] = '\0';
//
// Open the cat file as a CTL
//
if (!CryptQueryObject( CERT_QUERY_OBJECT_FILE, pwszCatalogFile, CERT_QUERY_CONTENT_FLAG_CTL, CERT_QUERY_FORMAT_FLAG_BINARY, 0, //flags
NULL, &dwContentType, NULL, NULL, NULL, (const void **) &pCTLContext)) { goto ErrorReturn; }
if (dwContentType != CERT_QUERY_CONTENT_CTL) { goto ErrorReturn; }
//
// Create another CTL context just like pCTLContext
//
CTLInfo = *(pCTLContext->pCtlInfo); CTLInfo.rgCTLEntry = (PCTL_ENTRY) new CTL_ENTRY[pCTLContext->pCtlInfo->cCTLEntry];
if (CTLInfo.rgCTLEntry == NULL) { goto ErrorReturn; }
//
// Loop through all the ctl entries and remove the entry
// that corresponds to the hash given
//
CTLInfo.cCTLEntry = 0; for (i=0; i<(int)pCTLContext->pCtlInfo->cCTLEntry; i++) { if (wcscmp( (LPWSTR) pCTLContext->pCtlInfo->rgCTLEntry[i].SubjectIdentifier.pbData, pwszHash) != 0) { CTLInfo.rgCTLEntry[CTLInfo.cCTLEntry++] = pCTLContext->pCtlInfo->rgCTLEntry[i]; } else { fHashFound = TRUE; } }
if (!fHashFound) { printf("<%S> not found in <%S>\n", pwszHash, pwszCatalogFile); goto ErrorReturn; }
//
// now save the CTL which is exactly the same as the previous one,
// except it doesn't doesn't have the hash being removed, back to
// the original filename
//
if (!CryptMsgEncodeAndSignCTL( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &CTLInfo, &signedInfo, 0, NULL, &cbEncodedCTL)) { goto ErrorReturn; }
if (NULL == (pbEncodedCTL = new BYTE[cbEncodedCTL])) { goto ErrorReturn; }
if (!CryptMsgEncodeAndSignCTL( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &CTLInfo, &signedInfo, 0, pbEncodedCTL, &cbEncodedCTL)) { goto ErrorReturn; }
if (INVALID_HANDLE_VALUE == (hFile = CreateFileW( pwszCatalogFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL))) { goto ErrorReturn; }
if (!WriteFile( hFile, pbEncodedCTL, cbEncodedCTL, &cbWritten, NULL)) { printf("WriteFile of <%S> failed with %x\n", pwszCatalogFile, GetLastError()); goto ErrorReturn; }
if (cbWritten != cbEncodedCTL) { goto ErrorReturn; }
CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE;
CommonReturn: if (pwszHash != NULL) { delete (pwszHash); }
if (pCTLContext != NULL) { CertFreeCTLContext(pCTLContext); }
if (CTLInfo.rgCTLEntry != NULL) { delete (CTLInfo.rgCTLEntry); }
if (pbEncodedCTL != NULL) { delete (pbEncodedCTL); }
if (hFile != INVALID_HANDLE_VALUE) { if (!CloseHandle(hFile)) { fRet = FALSE; } }
return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; }
//+---------------------------------------------------------------------------
//
// Function: CheckFileSize
//
// Synopsis: Checks that a file meets the minumum size requirement.
//
//----------------------------------------------------------------------------
BOOL CheckFileSize (LPWSTR fileName, ULONG sizeLimit) { HANDLE hFile; LARGE_INTEGER sizeFile = {0};
// Attempt to open the specified file
hFile = CreateFileW( fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL ); if (INVALID_HANDLE_VALUE == hFile) { printf("Error opening %S (%lu)\n", fileName, GetLastError() ); return FALSE; }
// Get the file size
if (!GetFileSizeEx(hFile, &sizeFile)) { printf("Error determining size of %S (%lu)\n", fileName, GetLastError()); return FALSE; }
if ((!sizeFile.HighPart) && (sizeFile.LowPart < sizeLimit)) { // File is too small
return FALSE; }
// Success. File is not too small.
return TRUE; }
|