//+--------------------------------------------------------------------------- // // Microsoft Windows NT Security // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: updcat.cpp // // Contents: Update Catalog Entry // // History: 02-Sep-98 kirtd Created // //---------------------------------------------------------------------------- #include #include #include "wincrypt.h" #include #include #include #include #include #include #include #include #include // 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 [-s ] [-a ]\n"); printf("Usage: updcat [-d ]\n"); printf("Usage: updcat [-s ] [-r ]\n"); printf("Usage: updcat [-attr ]\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; }