Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

663 lines
20 KiB

//+-------------------------------------------------------------------------
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 2001 - 2001
//
// File: vercat.cpp
//
// Contents: Minimal Cryptographic functions to verify hashes in the
// system catalogs.
//
// Functions: MinCryptVerifyHashInSystemCatalogs
//
// History: 23-Jan-01 philh created
//--------------------------------------------------------------------------
#include "global.hxx"
#include <softpub.h>
#include <mscat.h>
// #define szOID_CTL "1.3.6.1.4.1.311.10.1"
const BYTE rgbOID_CTL[] =
{0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0A, 0x01};
// #define SPC_INDIRECT_DATA_OBJID "1.3.6.1.4.1.311.2.1.4"
static const BYTE rgbSPC_INDIRECT_DATA_OBJID[] =
{0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04};
const CRYPT_DER_BLOB IndirectDataEncodedOIDBlob = {
sizeof(rgbSPC_INDIRECT_DATA_OBJID),
(BYTE *) rgbSPC_INDIRECT_DATA_OBJID
};
#define MAX_CAT_FILE_CNT 10
#define MAX_CAT_ATTR_CNT 10
#define MAX_CAT_EXT_CNT 10
typedef struct _MAP_CAT_INFO {
LONG lErr;
CRYPT_DATA_BLOB FileBlob;
CRYPT_DER_BLOB CTLSubjectsValueBlob;
CRYPT_DER_BLOB CTLExtsValueBlob;
CRYPT_DER_BLOB SignerAuthAttrsValueBlob;
} MAP_CAT_INFO, *PMAP_CAT_INFO;
#define MAP_CAT_IDX_MASK 0x0000FFFF
#define MAP_CAT_MULTIPLE_FLAG 0x00010000
LONG
WINAPI
I_GetAndMapSystemCatalogs(
IN ALG_ID HashAlgId,
IN DWORD cHash,
IN CRYPT_HASH_BLOB rgHashBlob[],
OUT LONG rglHashMapCatIdx[],
OUT DWORD *pcMapCatInfo,
OUT MAP_CAT_INFO rgMapCatInfo[MAX_CAT_FILE_CNT]
)
{
LONG lErr;
HCATADMIN hCatAdmin = NULL;
const GUID guidCatRoot = DRIVER_ACTION_VERIFY;
DWORD cMapCatInfo = 0;
DWORD cCatInfo = 0;
CATALOG_INFO rgCatInfo[MAX_CAT_FILE_CNT];
LONG rglMapCatIdx[MAX_CAT_FILE_CNT];
DWORD iHash;
if (!(CryptCATAdminAcquireContext(&hCatAdmin, &guidCatRoot, 0)))
goto CryptCATAdminAcquireContextError;
for (iHash = 0; iHash < cHash; iHash++) {
HCATINFO hCatInfo = NULL;
// Set index to indicate no catalog file
rglHashMapCatIdx[iHash] = -1;
while (hCatInfo = CryptCATAdminEnumCatalogFromHash(hCatAdmin,
rgHashBlob[iHash].pbData, rgHashBlob[iHash].cbData,
0, &hCatInfo)) {
CATALOG_INFO CatInfo;
LONG lMapCatIdx;
DWORD iCatInfo;
memset(&CatInfo, 0, sizeof(CATALOG_INFO));
CatInfo.cbStruct = sizeof(CATALOG_INFO);
if (!(CryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0)))
continue;
// Ensure we have a NULL terminated string
CatInfo.wszCatalogFile[
sizeof(CatInfo.wszCatalogFile)/sizeof(WCHAR) - 1] = L'\0';
// Check if we already encountered this catalog file
for (iCatInfo = 0; iCatInfo < cCatInfo; iCatInfo++) {
if (0 == _wcsicmp(CatInfo.wszCatalogFile,
rgCatInfo[iCatInfo].wszCatalogFile))
break;
}
if (iCatInfo >= cCatInfo) {
// Attempt to map this new catalog file
if (cCatInfo >= MAX_CAT_FILE_CNT)
continue;
wcscpy(rgCatInfo[cCatInfo].wszCatalogFile,
CatInfo.wszCatalogFile);
memset(&rgMapCatInfo[cMapCatInfo], 0, sizeof(MAP_CAT_INFO));
if (ERROR_SUCCESS == I_MinCryptMapFile(
MINCRYPT_FILE_NAME,
(const VOID *) CatInfo.wszCatalogFile,
&rgMapCatInfo[cMapCatInfo].FileBlob
))
rglMapCatIdx[cCatInfo] = cMapCatInfo++;
else
rglMapCatIdx[cCatInfo] = -1;
cCatInfo++;
assert(iCatInfo < cCatInfo);
}
lMapCatIdx = rglMapCatIdx[iCatInfo];
if (0 > rglHashMapCatIdx[iHash])
rglHashMapCatIdx[iHash] = lMapCatIdx;
else if (0 <= lMapCatIdx)
rglHashMapCatIdx[iHash] |= MAP_CAT_MULTIPLE_FLAG;
}
}
lErr = ERROR_SUCCESS;
CommonReturn:
*pcMapCatInfo = cMapCatInfo;
if (hCatAdmin)
CryptCATAdminReleaseContext(hCatAdmin, 0);
return lErr;
CryptCATAdminAcquireContextError:
lErr = GetLastError();
if (ERROR_SUCCESS == lErr)
lErr = E_UNEXPECTED;
goto CommonReturn;
}
VOID
WINAPI
I_VerifyMappedCatalog(
IN OUT PMAP_CAT_INFO pMapCatInfo
)
{
LONG lErr;
CRYPT_DER_BLOB rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_BLOB_CNT];
CRYPT_DER_BLOB rgCTLBlob[MINASN1_CTL_BLOB_CNT];
__try {
lErr = MinCryptVerifySignedData(
pMapCatInfo->FileBlob.pbData,
pMapCatInfo->FileBlob.cbData,
rgVerSignedDataBlob
);
if (ERROR_SUCCESS != lErr)
goto ErrorReturn;
// The data content should be a CTL
if (sizeof(rgbOID_CTL) !=
rgVerSignedDataBlob[
MINCRYPT_VER_SIGNED_DATA_CONTENT_OID_IDX].cbData
||
0 != memcmp(rgbOID_CTL,
rgVerSignedDataBlob[
MINCRYPT_VER_SIGNED_DATA_CONTENT_OID_IDX].pbData,
sizeof(rgbOID_CTL)))
goto NotCTLOID;
if (0 >= MinAsn1ParseCTL(
&rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_CONTENT_DATA_IDX],
rgCTLBlob
))
goto ParseCTLError;
pMapCatInfo->CTLSubjectsValueBlob =
rgCTLBlob[MINASN1_CTL_SUBJECTS_IDX];
pMapCatInfo->CTLExtsValueBlob =
rgCTLBlob[MINASN1_CTL_EXTS_IDX];
pMapCatInfo->SignerAuthAttrsValueBlob =
rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_AUTH_ATTRS_IDX];
} __except(EXCEPTION_EXECUTE_HANDLER) {
lErr = GetExceptionCode();
if (ERROR_SUCCESS == lErr)
lErr = E_UNEXPECTED;
goto ErrorReturn;
}
lErr = ERROR_SUCCESS;
CommonReturn:
pMapCatInfo->lErr = lErr;
return;
ErrorReturn:
goto CommonReturn;
NotCTLOID:
ParseCTLError:
lErr = CRYPT_E_BAD_MSG;
goto ErrorReturn;
}
VOID
WINAPI
I_VerifyMappedSystemCatalogs(
IN DWORD cMapCatInfo,
IN OUT MAP_CAT_INFO rgMapCatInfo[MAX_CAT_FILE_CNT]
)
{
DWORD i;
for (i = 0; i < cMapCatInfo; i++)
I_VerifyMappedCatalog(&rgMapCatInfo[i]);
}
LONG
WINAPI
I_FindHashInCTLSubjects(
IN ALG_ID HashAlgId,
IN PCRYPT_HASH_BLOB pHashBlob,
IN PCRYPT_DER_BLOB pCTLSubjectsValueBlob,
OUT PCRYPT_DER_BLOB pCTLSubjectAttrsValueBlob
)
{
DWORD cbEncoded;
const BYTE *pbEncoded;
// Advance past the outer tag and length
if (0 >= MinAsn1ExtractContent(
pCTLSubjectsValueBlob->pbData,
pCTLSubjectsValueBlob->cbData,
&cbEncoded,
&pbEncoded
))
goto NoOrInvalidSubjects;
while (cbEncoded) {
// Loop through the encoded subjects until we have a hash match
// with the digest octets in the IndirectData attribute.
LONG cbSubject;
CRYPT_DER_BLOB rgCTLSubjectBlob[MINASN1_CTL_SUBJECT_BLOB_CNT];
DWORD cAttr;
CRYPT_DER_BLOB rgrgAttrBlob[MAX_CAT_ATTR_CNT][MINASN1_ATTR_BLOB_CNT];
cbSubject = MinAsn1ParseCTLSubject(
pbEncoded,
cbEncoded,
rgCTLSubjectBlob
);
if (0 >= cbSubject)
goto InvalidSubject;
cAttr = MAX_CAT_ATTR_CNT;
if (0 < MinAsn1ParseAttributes(
&rgCTLSubjectBlob[MINASN1_CTL_SUBJECT_ATTRS_IDX],
&cAttr,
rgrgAttrBlob
)) {
PCRYPT_DER_BLOB rgIndirectDataAttrBlob;
CRYPT_DER_BLOB rgIndirectDataBlob[MINASN1_INDIRECT_DATA_BLOB_CNT];
rgIndirectDataAttrBlob = MinAsn1FindAttribute(
(PCRYPT_DER_BLOB) &IndirectDataEncodedOIDBlob,
cAttr,
rgrgAttrBlob
);
if (rgIndirectDataAttrBlob &&
0 < MinAsn1ParseIndirectData(
&rgIndirectDataAttrBlob[MINASN1_ATTR_VALUE_IDX],
rgIndirectDataBlob)) {
if (pHashBlob->cbData ==
rgIndirectDataBlob[
MINASN1_INDIRECT_DATA_DIGEST_IDX].cbData
&&
0 == memcmp(pHashBlob->pbData,
rgIndirectDataBlob[
MINASN1_INDIRECT_DATA_DIGEST_IDX].pbData,
pHashBlob->cbData)) {
*pCTLSubjectAttrsValueBlob =
rgCTLSubjectBlob[MINASN1_CTL_SUBJECT_ATTRS_IDX];
return ERROR_SUCCESS;
}
}
}
pbEncoded += cbSubject;
cbEncoded -= cbSubject;
}
NoOrInvalidSubjects:
InvalidSubject:
pCTLSubjectAttrsValueBlob->pbData = NULL;
pCTLSubjectAttrsValueBlob->cbData = 0;
return ERROR_FILE_NOT_FOUND;
}
LONG
WINAPI
I_FindHashInMappedSystemCatalogs(
IN ALG_ID HashAlgId,
IN PCRYPT_HASH_BLOB pHashBlob,
IN DWORD cMapCatInfo,
IN MAP_CAT_INFO rgMapCatInfo[MAX_CAT_FILE_CNT],
IN OUT LONG *plMapCatIdx,
OUT PCRYPT_DER_BLOB pCTLSubjectAttrsValueBlob
)
{
LONG lErr;
LONG lMapCatIdx = *plMapCatIdx;
BOOL fMultiple = FALSE;
if (0 > lMapCatIdx)
goto NotInCatalog;
if (lMapCatIdx & MAP_CAT_MULTIPLE_FLAG)
fMultiple = TRUE;
else
fMultiple = FALSE;
lMapCatIdx &= MAP_CAT_IDX_MASK;
assert((DWORD) lMapCatIdx < cMapCatInfo);
if (ERROR_SUCCESS == rgMapCatInfo[lMapCatIdx].lErr
&&
ERROR_SUCCESS == I_FindHashInCTLSubjects(
HashAlgId,
pHashBlob,
&rgMapCatInfo[lMapCatIdx].CTLSubjectsValueBlob,
pCTLSubjectAttrsValueBlob
))
goto SuccessReturn;
if (fMultiple) {
DWORD i;
for (i = 0; i < cMapCatInfo; i++) {
if ((DWORD) lMapCatIdx == i)
continue;
if (ERROR_SUCCESS == rgMapCatInfo[i].lErr
&&
ERROR_SUCCESS == I_FindHashInCTLSubjects(
HashAlgId,
pHashBlob,
&rgMapCatInfo[i].CTLSubjectsValueBlob,
pCTLSubjectAttrsValueBlob
)) {
lMapCatIdx = i;
goto SuccessReturn;
}
}
}
NotInCatalog:
lErr = ERROR_FILE_NOT_FOUND;
lMapCatIdx = -1;
pCTLSubjectAttrsValueBlob->pbData = NULL;
pCTLSubjectAttrsValueBlob->cbData = 0;
goto CommonReturn;
SuccessReturn:
lErr = ERROR_SUCCESS;
CommonReturn:
*plMapCatIdx = lMapCatIdx;
return lErr;
}
VOID
WINAPI
I_GetHashAttributes(
IN OPTIONAL DWORD cAttrOID,
IN OPTIONAL CRYPT_DER_BLOB rgAttrEncodedOIDBlob[],
IN PCRYPT_DER_BLOB pCTLSubjectAttrsValueBlob,
IN PCRYPT_DER_BLOB pCTLExtsValueBlob,
IN PCRYPT_DER_BLOB pSignerAuthAttrsValueBlob,
IN OUT OPTIONAL CRYPT_DER_BLOB rgAttrValueBlob[],
IN OUT LONG *plRemainExtra,
IN OUT BYTE **ppbExtra
)
{
DWORD cSubjectAttr;
CRYPT_DER_BLOB rgrgSubjectAttrBlob[MAX_CAT_ATTR_CNT][MINASN1_ATTR_BLOB_CNT];
DWORD cExt;
CRYPT_DER_BLOB rgrgExtBlob[MAX_CAT_EXT_CNT][MINASN1_EXT_BLOB_CNT];
DWORD cSignerAttr;
CRYPT_DER_BLOB rgrgSignerAttrBlob[MAX_CAT_ATTR_CNT][MINASN1_ATTR_BLOB_CNT];
DWORD i;
LONG lRemainExtra = *plRemainExtra;
BYTE *pbExtra = *ppbExtra;
// Parse the attributes and extensions
cSubjectAttr = MAX_CAT_ATTR_CNT;
if (0 >= MinAsn1ParseAttributes(
pCTLSubjectAttrsValueBlob,
&cSubjectAttr,
rgrgSubjectAttrBlob))
cSubjectAttr = 0;
cExt = MAX_CAT_EXT_CNT;
if (0 >= MinAsn1ParseExtensions(
pCTLExtsValueBlob,
&cExt,
rgrgExtBlob))
cExt = 0;
cSignerAttr = MAX_CAT_ATTR_CNT;
if (0 >= MinAsn1ParseAttributes(
pSignerAuthAttrsValueBlob,
&cSignerAttr,
rgrgSignerAttrBlob))
cSignerAttr = 0;
for (i = 0; i < cAttrOID; i++) {
PCRYPT_DER_BLOB rgFindAttrBlob;
PCRYPT_DER_BLOB rgFindExtBlob;
PCRYPT_DER_BLOB pFindAttrValue;
if (rgFindAttrBlob = MinAsn1FindAttribute(
&rgAttrEncodedOIDBlob[i],
cSubjectAttr,
rgrgSubjectAttrBlob
))
pFindAttrValue = &rgFindAttrBlob[MINASN1_ATTR_VALUE_IDX];
else if (rgFindExtBlob = MinAsn1FindExtension(
&rgAttrEncodedOIDBlob[i],
cExt,
rgrgExtBlob
))
pFindAttrValue = &rgFindExtBlob[MINASN1_EXT_VALUE_IDX];
else if (rgFindAttrBlob = MinAsn1FindAttribute(
&rgAttrEncodedOIDBlob[i],
cSignerAttr,
rgrgSignerAttrBlob
))
pFindAttrValue = &rgFindAttrBlob[MINASN1_ATTR_VALUE_IDX];
else
pFindAttrValue = NULL;
if (pFindAttrValue && 0 != pFindAttrValue->cbData) {
const BYTE *pbFindValue = pFindAttrValue->pbData;
DWORD cbFindValue = pFindAttrValue->cbData;
lRemainExtra -= cbFindValue;
if (0 <= lRemainExtra) {
rgAttrValueBlob[i].pbData = pbExtra;
rgAttrValueBlob[i].cbData = cbFindValue;
memcpy(pbExtra, pbFindValue, cbFindValue);
pbExtra += cbFindValue;
}
}
}
*plRemainExtra = lRemainExtra;
*ppbExtra = pbExtra;
}
//+-------------------------------------------------------------------------
// Verifies the hashes in the system catalogs.
//
// Iterates through the hashes and attempts to find the system catalog
// containing it. If found, the system catalog file is verified as a
// PKCS #7 Signed Data message with its signer cert verified up to a baked
// in root.
//
// The following mscat32.dll APIs are called to find the system catalog file:
// CryptCATAdminAcquireContext
// CryptCATAdminReleaseContext
// CryptCATAdminEnumCatalogFromHash
// CryptCATAdminReleaseCatalogContext
// CryptCATCatalogInfoFromContext
//
// If the hash was successfully verified, rglErr[] is set to ERROR_SUCCESS.
// Otherwise, rglErr[] is set to a nonzero error code.
//
// The caller can request one or more catalog subject attribute,
// extension or signer authenticated attribute values to be returned for
// each hash. The still encoded values are returned in the
// caller allocated memory. The beginning of this returned memory will
// be set to a 2 dimensional array of attribute value blobs pointing to these
// encoded values (CRYPT_DER_BLOB rgrgAttrValueBlob[cHash][cAttrOID]).
// The caller should make every attempt to allow for a
// single pass call. The necessary memory size is:
// (cHash * cAttrOID * sizeof(CRYPT_DER_BLOB)) +
// total length of encoded attribute values.
//
// *pcbAttr will be updated with the number of bytes required to contain
// the attribute blobs and values. If the input memory is insufficient,
// ERROR_INSUFFICIENT_BUFFER will be returned if no other error.
//
// For a multi-valued attribute, only the first value is returned.
//
// If the function succeeds, the return value is ERROR_SUCCESS. This may
// be returned for unsuccessful rglErr[] values. Otherwise,
// a nonzero error code is returned.
//--------------------------------------------------------------------------
LONG
WINAPI
MinCryptVerifyHashInSystemCatalogs(
IN ALG_ID HashAlgId,
IN DWORD cHash,
IN CRYPT_HASH_BLOB rgHashBlob[],
OUT LONG rglErr[],
IN OPTIONAL DWORD cAttrOID,
IN OPTIONAL CRYPT_DER_BLOB rgAttrEncodedOIDBlob[],
// CRYPT_DER_BLOB rgrgAttrValueBlob[cHash][cAttrOID] header is at beginning
// with the bytes pointed to immediately following
OUT OPTIONAL CRYPT_DER_BLOB *rgrgAttrValueBlob,
IN OUT OPTIONAL DWORD *pcbAttr
)
{
LONG lErr;
DWORD cMapCatInfo = 0;
MAP_CAT_INFO rgMapCatInfo[MAX_CAT_FILE_CNT];
DWORD iMapCat;
//**********************************************************************
// WARNING!!!!
//
// The following function calls into other DLLs such as, kernel32.dll
// and wintrust.dll to find and map the system catalog files. The input
// array of hashes must be protected!!
//
// After returning we won't be calling into other DLLs until
// UnmapViewOfFile is called in CommonReturn.
//
//**********************************************************************
// Note, rglErr[] is overloaded and also used to contain the indices
// into rgMapCatInfo for each corresponding hash.
lErr = I_GetAndMapSystemCatalogs(
HashAlgId,
cHash,
rgHashBlob,
rglErr,
&cMapCatInfo,
rgMapCatInfo
);
if (ERROR_SUCCESS != lErr)
goto ErrorReturn;
__try {
DWORD cbAttr = 0;
LONG lRemainExtra = 0;
BYTE *pbExtra = NULL;
DWORD iHash;
if (0 != cAttrOID && 0 != cHash) {
if (rgrgAttrValueBlob)
cbAttr = *pcbAttr;
lRemainExtra = cbAttr - sizeof(CRYPT_DER_BLOB) * cAttrOID * cHash;
if (0 <= lRemainExtra) {
memset(rgrgAttrValueBlob, 0,
sizeof(CRYPT_DER_BLOB) * cAttrOID * cHash);
pbExtra = (BYTE *) &rgrgAttrValueBlob[cAttrOID * cHash];
}
}
I_VerifyMappedSystemCatalogs(
cMapCatInfo,
rgMapCatInfo
);
for (iHash = 0; iHash < cHash; iHash++) {
LONG lMapCatIdx = rglErr[iHash];
CRYPT_DER_BLOB CTLSubjectAttrsValueBlob;
rglErr[iHash] = I_FindHashInMappedSystemCatalogs(
HashAlgId,
&rgHashBlob[iHash],
cMapCatInfo,
rgMapCatInfo,
&lMapCatIdx,
&CTLSubjectAttrsValueBlob
);
if (0 != cAttrOID && ERROR_SUCCESS == rglErr[iHash]) {
assert(0 <= lMapCatIdx && (DWORD) lMapCatIdx < cMapCatInfo);
I_GetHashAttributes(
cAttrOID,
rgAttrEncodedOIDBlob,
&CTLSubjectAttrsValueBlob,
&rgMapCatInfo[lMapCatIdx].CTLExtsValueBlob,
&rgMapCatInfo[lMapCatIdx].SignerAuthAttrsValueBlob,
0 <= lRemainExtra ?
&rgrgAttrValueBlob[cAttrOID * iHash] : NULL,
&lRemainExtra,
&pbExtra
);
}
}
if (0 != cAttrOID && 0 != cHash) {
if (0 <= lRemainExtra)
*pcbAttr = cbAttr - (DWORD) lRemainExtra;
else {
*pcbAttr = cbAttr + (DWORD) -lRemainExtra;
goto InsufficientBuffer;
}
}
} __except(EXCEPTION_EXECUTE_HANDLER) {
lErr = GetExceptionCode();
if (ERROR_SUCCESS == lErr)
lErr = E_UNEXPECTED;
goto ErrorReturn;
}
lErr = ERROR_SUCCESS;
CommonReturn:
//**********************************************************************
// WARNING!!!!
//
// UnmapViewOfFile is in another DLL, kernel32.dll.
// lErr and the return error for each hash in rglErr[] must be protected.
//
//**********************************************************************
for (iMapCat = 0; iMapCat < cMapCatInfo; iMapCat++)
UnmapViewOfFile(rgMapCatInfo[iMapCat].FileBlob.pbData);
return lErr;
ErrorReturn:
assert(ERROR_SUCCESS != lErr);
if (ERROR_INSUFFICIENT_BUFFER == lErr)
// This error can only be set when we determine that the attribute
// buffer isn't big enough.
lErr = E_UNEXPECTED;
goto CommonReturn;
InsufficientBuffer:
lErr = ERROR_INSUFFICIENT_BUFFER;
// Don't goto ErrorReturn. It explicitly checks that noone else can
// set this error
goto CommonReturn;
}