Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1491 lines
39 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows NT Security
// Copyright (C) Microsoft Corporation, 1992 - 1999
//
// File: ekuhlpr.cpp
//
// Contents: Certificate Enhanced Key Usage Helper API implementation
//
// History: 21-May-97 kirtd Created
// xx-xxx-xx reidk Added CertGetValidUsages
//
//----------------------------------------------------------------------------
#include <global.hxx>
#include <dbgdef.h>
//+---------------------------------------------------------------------------
//
// Function: CertGetEnhancedKeyUsage
//
// Synopsis: gets the enhanced key usage extension/property from the
// certificate
//
//----------------------------------------------------------------------------
BOOL WINAPI CertGetEnhancedKeyUsage (
IN PCCERT_CONTEXT pCertContext,
IN DWORD dwFlags,
OUT PCERT_ENHKEY_USAGE pUsage,
IN OUT DWORD* pcbUsage
)
{
HRESULT hr = S_OK;
CRYPT_OBJID_BLOB cob;
BOOL fExtCertPolicies = FALSE;
PCRYPT_OBJID_BLOB pExtBlob = NULL;
PCRYPT_OBJID_BLOB pPropBlob = NULL;
//
// If the flags are zero then assume they want everything
//
if ( dwFlags == 0 )
{
dwFlags = CERT_FIND_ALL_ENHKEY_USAGE_FLAG;
}
//
// Validate the parameters
//
if ( ( ( dwFlags & CERT_FIND_ALL_ENHKEY_USAGE_FLAG ) == 0 ) ||
( pCertContext == NULL ) || ( pcbUsage == NULL ) )
{
SetLastError((DWORD) ERROR_INVALID_PARAMETER);
return( FALSE );
}
//
// If they want everything, call CertGetValidUsages
//
if ( dwFlags == CERT_FIND_ALL_ENHKEY_USAGE_FLAG )
{
return( EkuGetIntersectedUsageViaGetValidUsages(
pCertContext,
pcbUsage,
pUsage
) );
}
//
// If they want extensions get the extension blob, if they want
// properties get the property blob
//
if ( dwFlags & CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG )
{
pExtBlob = EkuGetExtension(pCertContext, &fExtCertPolicies);
}
if ( dwFlags & CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG )
{
hr = EkuGetProperty(pCertContext, &cob);
if ( hr == S_OK )
{
pPropBlob = &cob;
}
}
//
// Make sure that at least something was found and that what has occurred
// is correctly indicated
//
if ( ( pExtBlob == NULL ) && ( pPropBlob == NULL ) )
{
if ( hr == S_OK )
{
hr = CRYPT_E_NOT_FOUND;
}
}
else
{
hr = S_OK;
}
//
// If all they wanted was the size, give it to them, otherwise, we
// need to decode and give the caller what they requested
//
if ( hr == S_OK )
{
if ( pUsage == NULL )
{
DWORD cbSize = 0;
DWORD cbExtSize = 0;
DWORD cbPropSize = 0;
hr = EkuGetDecodedUsageSizes(
fExtCertPolicies,
pExtBlob,
pPropBlob,
&cbSize,
&cbExtSize,
&cbPropSize
);
if ( hr == S_OK )
{
if ( cbSize > 0 )
{
*pcbUsage = cbSize;
}
else
{
// Need better last error code
hr = E_INVALIDARG;
}
}
}
else
{
hr = EkuGetMergedDecodedUsage(
fExtCertPolicies,
pExtBlob,
pPropBlob,
pcbUsage,
pUsage
);
}
}
//
// Cleanup and return
//
if ( pPropBlob != NULL )
{
delete pPropBlob->pbData;
}
if ( hr != S_OK )
{
SetLastError(hr);
return( FALSE );
}
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: CertSetEnhancedKeyUsage
//
// Synopsis: sets the enhanced key usage property on the certificate
//
//----------------------------------------------------------------------------
BOOL WINAPI CertSetEnhancedKeyUsage (
IN PCCERT_CONTEXT pCertContext,
IN PCERT_ENHKEY_USAGE pUsage
)
{
HRESULT hr;
CRYPT_OBJID_BLOB EkuBlob;
//
// if pUsage is NULL, then just set the NULL property
//
if (pUsage == NULL)
{
hr = EkuSetProperty(pCertContext, NULL);
}
else
{
//
// Encode the usage and set the property
//
hr = EkuEncodeUsage(pUsage, &EkuBlob);
if ( hr == S_OK )
{
hr = EkuSetProperty(pCertContext, &EkuBlob);
delete EkuBlob.pbData;
}
}
if ( hr != S_OK )
{
SetLastError(hr);
return( FALSE );
}
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: CertAddEnhancedKeyUsageIdentifier
//
// Synopsis: adds a key usage identifier to the enhanced key usage property
// on the certificate
//
//----------------------------------------------------------------------------
BOOL WINAPI CertAddEnhancedKeyUsageIdentifier (
IN PCCERT_CONTEXT pCertContext,
IN LPCSTR pszUsageIdentifier
)
{
HRESULT hr;
DWORD cbUsage1 = 0;
DWORD cbUsage2 = 0;
DWORD cbUsageM = 0;
DWORD cId;
PCERT_ENHKEY_USAGE pUsage1 = NULL;
PCERT_ENHKEY_USAGE pUsage2 = NULL;
PCERT_ENHKEY_USAGE pUsageM = NULL;
//
// Create a one element, properly "encoded" (see EkuMergeUsage) enhanced
// key usage structure
//
cId = strlen(pszUsageIdentifier)+1;
cbUsage1 = sizeof(CERT_ENHKEY_USAGE)+sizeof(LPSTR)+cId;
pUsage1 = (PCERT_ENHKEY_USAGE)new BYTE [cbUsage1];
if ( pUsage1 == NULL )
{
SetLastError((DWORD) E_OUTOFMEMORY);
return( FALSE );
}
pUsage1->cUsageIdentifier = 1;
pUsage1->rgpszUsageIdentifier = (LPSTR *)((LPBYTE)pUsage1+sizeof(CERT_ENHKEY_USAGE));
pUsage1->rgpszUsageIdentifier[0] = (LPSTR)((LPBYTE)pUsage1->rgpszUsageIdentifier+sizeof(LPSTR));
strcpy(pUsage1->rgpszUsageIdentifier[0], pszUsageIdentifier);
//
// Get the current enhanced key usage properties and get an appropriately
// sized block for the merged data unless there are no current usage
// properties in which case we just set the one we have now
//
hr = EkuGetUsage(
pCertContext,
CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
&cbUsage2,
&pUsage2
);
if ( hr == S_OK )
{
cbUsageM = cbUsage1 + cbUsage2;
pUsageM = (PCERT_ENHKEY_USAGE)new BYTE [cbUsageM];
if ( pUsageM == NULL )
{
hr = E_OUTOFMEMORY;
}
}
else if ( hr == CRYPT_E_NOT_FOUND )
{
BOOL fReturn;
fReturn = CertSetEnhancedKeyUsage(pCertContext, pUsage1);
delete pUsage1;
return( fReturn );
}
else
{
SetLastError(hr);
return( FALSE );
}
//
// Merge the usage structures and set the properties
//
hr = EkuMergeUsage(cbUsage1, pUsage1, cbUsage2, pUsage2, cbUsageM, pUsageM);
if ( hr == S_OK )
{
if ( CertSetEnhancedKeyUsage(pCertContext, pUsageM) == FALSE )
{
hr = GetLastError();
}
}
//
// Cleanup
//
delete pUsage1;
delete pUsage2;
delete pUsageM;
if ( hr != S_OK )
{
SetLastError(hr);
return( FALSE );
}
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: CertRemoveEnhancedKeyUsageIdentifier
//
// Synopsis: removes a key usage identifier from the enhanced key usage
// property on the certificate
//
//----------------------------------------------------------------------------
BOOL WINAPI CertRemoveEnhancedKeyUsageIdentifier (
IN PCCERT_CONTEXT pCertContext,
IN LPCSTR pszUsageIdentifier
)
{
HRESULT hr;
DWORD cFound = 0;
DWORD cCount;
PCERT_ENHKEY_USAGE pUsage;
LPSTR* apsz;
//
// Get the current usage properties
//
hr = EkuGetUsage(
pCertContext,
CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
NULL,
&pUsage
);
if ( hr != S_OK )
{
SetLastError(hr);
return( FALSE );
}
//
// Loop through the usage identifiers and remove ones that match
// the passed in id
//
apsz = pUsage->rgpszUsageIdentifier;
for (cCount = 0; cCount < pUsage->cUsageIdentifier; cCount++)
{
if ( strcmp(apsz[cCount], pszUsageIdentifier) == 0 )
{
cFound++;
}
else if ( cFound > 0 )
{
apsz[cCount-cFound] = apsz[cCount];
}
}
//
// If we removed any, update the usage id count and set the new property
//
if ( cFound > 0 )
{
pUsage->cUsageIdentifier -= cFound;
if ( pUsage->cUsageIdentifier == 0 )
{
// Delete the property if we are down to zero
hr = EkuSetProperty(pCertContext, NULL);
}
else if ( CertSetEnhancedKeyUsage(pCertContext, pUsage) == FALSE )
{
hr = GetLastError();
}
}
//
// Cleanup
//
delete pUsage;
if ( hr != S_OK )
{
SetLastError(hr);
return( FALSE );
}
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: EkuGetExtension
//
// Synopsis: gets the application cert policies or enhanced key usage
// extension blob from the certificate
//
// *pfAppCertPolicies is set to TRUE for an
// szOID_APPLICATION_CERT_POLICIES extension.
//
//----------------------------------------------------------------------------
PCRYPT_OBJID_BLOB EkuGetExtension (
PCCERT_CONTEXT pCertContext,
BOOL *pfAppCertPolicies
)
{
PCERT_EXTENSION pExtension;
//
// Get the application cert policies or enhanced key usage extension
// from the certificate and if we couldn't find either
// extension return NULL otherwise, return
// the appropriate field of the found extension
//
pExtension = CertFindExtension(
szOID_APPLICATION_CERT_POLICIES,
pCertContext->pCertInfo->cExtension,
pCertContext->pCertInfo->rgExtension
);
if ( pExtension )
{
*pfAppCertPolicies = TRUE;
}
else
{
*pfAppCertPolicies = FALSE;
pExtension = CertFindExtension(
szOID_ENHANCED_KEY_USAGE,
pCertContext->pCertInfo->cExtension,
pCertContext->pCertInfo->rgExtension
);
if ( pExtension == NULL )
{
return( NULL );
}
}
return( &pExtension->Value );
}
//+---------------------------------------------------------------------------
//
// Function: EkuGetProperty
//
// Synopsis: gets the enhanced key usage property from the certificate
//
//----------------------------------------------------------------------------
HRESULT EkuGetProperty (
PCCERT_CONTEXT pCertContext,
PCRYPT_OBJID_BLOB pEkuBlob
)
{
DWORD cb;
if ( CertGetCertificateContextProperty(
pCertContext,
CERT_ENHKEY_USAGE_PROP_ID,
NULL,
&cb
) == FALSE )
{
return( GetLastError() );
}
pEkuBlob->cbData = cb;
pEkuBlob->pbData = new BYTE [cb];
if ( pEkuBlob->pbData == NULL )
{
return( E_OUTOFMEMORY );
}
if ( CertGetCertificateContextProperty(
pCertContext,
CERT_ENHKEY_USAGE_PROP_ID,
pEkuBlob->pbData,
&cb
) == FALSE )
{
return( GetLastError() );
}
return( S_OK );
}
//+---------------------------------------------------------------------------
//
// Function: EkuSetProperty
//
// Synopsis: sets an enhanced key usage property on the certificate
//
//----------------------------------------------------------------------------
HRESULT EkuSetProperty (
PCCERT_CONTEXT pCertContext,
PCRYPT_OBJID_BLOB pEkuBlob
)
{
if ( CertSetCertificateContextProperty(
pCertContext,
CERT_ENHKEY_USAGE_PROP_ID,
0,
pEkuBlob
) == FALSE )
{
return( GetLastError() );
}
return( S_OK );
}
//+---------------------------------------------------------------------------
//
// Function: EkuDecodeCertPoliciesAndConvertToUsage
//
// Synopsis: decodes an encoded cert policies and converts to enhanced
// key usage
//
//----------------------------------------------------------------------------
HRESULT EkuDecodeCertPoliciesAndConvertToUsage (
PCRYPT_OBJID_BLOB pEkuBlob,
DWORD* pcbSize,
PCERT_ENHKEY_USAGE pUsage // OPTIONAL
)
{
HRESULT hr = S_OK;
DWORD cbCertPolicies = 0;
PCERT_POLICIES_INFO pCertPolicies = NULL;
DWORD cbSize = 0;
if ( !CryptDecodeObject(
X509_ASN_ENCODING,
X509_CERT_POLICIES,
pEkuBlob->pbData,
pEkuBlob->cbData,
CRYPT_DECODE_NOCOPY_FLAG |
CRYPT_DECODE_SHARE_OID_STRING_FLAG |
CRYPT_DECODE_ALLOC_FLAG,
(void *) &pCertPolicies,
&cbCertPolicies
))
{
hr = GetLastError();
}
else
{
// Convert policies OIDs to EKU OIDs
LONG lRemainExtra;
DWORD cOID;
LPSTR *ppszOID;
LPSTR pszOID;
PCERT_POLICY_INFO pPolicy;
cOID = pCertPolicies->cPolicyInfo;
pPolicy = pCertPolicies->rgPolicyInfo;
if ( pUsage )
{
cbSize = *pcbSize;
}
lRemainExtra = cbSize - sizeof(CERT_ENHKEY_USAGE) -
sizeof(LPSTR) * cOID;
if ( lRemainExtra < 0 )
{
ppszOID = NULL;
pszOID = NULL;
}
else
{
ppszOID = (LPSTR *) &pUsage[1];
pszOID = (LPSTR) &ppszOID[cOID];
pUsage->cUsageIdentifier = cOID;
pUsage->rgpszUsageIdentifier = ppszOID;
}
for ( ; cOID > 0; cOID--, ppszOID++, pPolicy++ )
{
DWORD cchOID;
cchOID = strlen(pPolicy->pszPolicyIdentifier) + 1;
lRemainExtra -= cchOID;
if ( lRemainExtra >= 0 )
{
*ppszOID = pszOID;
memcpy(pszOID, pPolicy->pszPolicyIdentifier, cchOID);
pszOID += cchOID;
}
}
if ( lRemainExtra >= 0)
{
cbSize -= (DWORD) lRemainExtra;
}
else
{
cbSize += (DWORD) -lRemainExtra;
if ( pUsage )
{
hr = ERROR_MORE_DATA;
}
}
}
if ( pCertPolicies )
{
LocalFree( pCertPolicies );
}
*pcbSize = cbSize;
return( hr );
}
//+---------------------------------------------------------------------------
//
// Function: EkuGetDecodedSize
//
// Synopsis: gets the decoded size of the enhanced key usage blob
//
//----------------------------------------------------------------------------
HRESULT EkuGetDecodedSize (
PCRYPT_OBJID_BLOB pEkuBlob,
DWORD* pcbSize
)
{
if ( CryptDecodeObject(
X509_ASN_ENCODING,
szOID_ENHANCED_KEY_USAGE,
pEkuBlob->pbData,
pEkuBlob->cbData,
0,
NULL,
pcbSize
) == FALSE )
{
return( GetLastError() );
}
return( S_OK );
}
//+---------------------------------------------------------------------------
//
// Function: EkuGetDecodedUsageSizes
//
// Synopsis: gets the decoded sizes for enhanced key usage blobs from the
// certificate extension and/or the certificate context property
//
//----------------------------------------------------------------------------
HRESULT EkuGetDecodedUsageSizes (
BOOL fExtCertPolicies,
PCRYPT_OBJID_BLOB pExtBlob,
PCRYPT_OBJID_BLOB pPropBlob,
DWORD* pcbSize,
DWORD* pcbExtSize,
DWORD* pcbPropSize
)
{
HRESULT hr = S_OK;
DWORD cbExtSize = 0;
DWORD cbPropSize = 0;
//
// Get the appropriate decoded size based on what was requested
//
if ( pExtBlob != NULL )
{
if ( fExtCertPolicies )
{
hr = EkuDecodeCertPoliciesAndConvertToUsage(
pExtBlob, &cbExtSize, NULL);
}
else
{
hr = EkuGetDecodedSize(pExtBlob, &cbExtSize);
}
}
if ( ( hr == S_OK ) && ( pPropBlob != NULL ) )
{
hr = EkuGetDecodedSize(pPropBlob, &cbPropSize);
}
//
// Collect into the out parameters
//
if ( hr == S_OK )
{
*pcbExtSize = cbExtSize;
*pcbPropSize = cbPropSize;
*pcbSize = cbExtSize + cbPropSize;
}
return( hr );
}
//+---------------------------------------------------------------------------
//
// Function: EkuGetDecodedUsage
//
// Synopsis: gets the decoded enhanced key usage from the encoded blob
//
//----------------------------------------------------------------------------
HRESULT EkuGetDecodedUsage (
PCRYPT_OBJID_BLOB pEkuBlob,
DWORD* pcbSize,
PCERT_ENHKEY_USAGE pUsage
)
{
if ( CryptDecodeObject(
X509_ASN_ENCODING,
szOID_ENHANCED_KEY_USAGE,
pEkuBlob->pbData,
pEkuBlob->cbData,
0,
pUsage,
pcbSize
) == FALSE )
{
return( GetLastError() );
}
return( S_OK );
}
//+---------------------------------------------------------------------------
//
// Function: EkuMergeUsage
//
// Synopsis: merges enhanced key usage structures
//
// NOTE: The structures are assumed to be in single allocated
// block form where the string pointers point back into
// the bottom part of the allocated block where the
// have been placed
//
//----------------------------------------------------------------------------
HRESULT EkuMergeUsage (
DWORD cbSize1,
PCERT_ENHKEY_USAGE pUsage1,
DWORD cbSize2,
PCERT_ENHKEY_USAGE pUsage2,
DWORD cbSizeM,
PCERT_ENHKEY_USAGE pUsageM
)
{
DWORD cUsage1;
DWORD cUsage2;
DWORD cUsageM;
DWORD cbOids1;
DWORD cbOids2;
DWORD cbUsage1;
DWORD cbUsage2;
DWORD cCount;
DWORD cbOffset;
LPSTR* apsz1;
LPSTR* apsz2;
LPSTR* apszM;
//
// Copy the data from the source to the destination
//
cUsage1 = pUsage1->cUsageIdentifier;
cUsage2 = pUsage2->cUsageIdentifier;
cUsageM = cUsage1 + cUsage2;
cbUsage1 = ( cUsage1 * sizeof(LPSTR) ) + sizeof(CERT_ENHKEY_USAGE);
cbUsage2 = ( cUsage2 * sizeof(LPSTR) ) + sizeof(CERT_ENHKEY_USAGE);
apsz1 = pUsage1->rgpszUsageIdentifier;
apsz2 = pUsage2->rgpszUsageIdentifier;
apszM = (LPSTR *)((LPBYTE)pUsageM+sizeof(CERT_ENHKEY_USAGE));
pUsageM->cUsageIdentifier = cUsageM;
pUsageM->rgpszUsageIdentifier = apszM;
memcpy(apszM, apsz1, cUsage1*sizeof(LPSTR));
memcpy(&apszM[cUsage1], apsz2, cUsage2*sizeof(LPSTR));
cbOids1 = cbSize1 - cbUsage1;
cbOids2 = cbSize2 - cbUsage2;
memcpy(&apszM[cUsageM], &apsz1[cUsage1], cbOids1);
memcpy(
(LPBYTE)(&apszM[cUsageM])+cbOids1,
&apsz2[cUsage2],
cbOids2
);
//
// Fix up the pointers
//
for ( cCount = 0; cCount < cUsage1; cCount++)
{
cbOffset = (DWORD)((LPBYTE)(apsz1[cCount]) - (LPBYTE)apsz1) + cbUsage2;
apszM[cCount] = (LPSTR)((LPBYTE)pUsageM+cbOffset);
}
for ( cCount = 0; cCount < cUsage2; cCount++ )
{
cbOffset = (DWORD)((LPBYTE)(apsz2[cCount]) - (LPBYTE)apsz2) + cbUsage1 + cbOids1;
apszM[cCount+cUsage1] = (LPSTR)((LPBYTE)pUsageM+cbOffset);
}
return( S_OK );
}
//+---------------------------------------------------------------------------
//
// Function: EkuGetMergedDecodedUsage
//
// Synopsis: gets merged decoded enhanced key usage from the certificate
// extension and the certificate properties
//
//----------------------------------------------------------------------------
HRESULT EkuGetMergedDecodedUsage (
BOOL fExtCertPolicies,
PCRYPT_OBJID_BLOB pExtBlob,
PCRYPT_OBJID_BLOB pPropBlob,
DWORD* pcbSize,
PCERT_ENHKEY_USAGE pUsage
)
{
HRESULT hr;
DWORD cbExtSize = 0;
DWORD cbPropSize = 0;
DWORD cbMergedSize = 0;
PCERT_ENHKEY_USAGE pExtUsage = NULL;
PCERT_ENHKEY_USAGE pPropUsage = NULL;
//
// If either the extension or the properties are NULL, we just need
// to get the other one
//
if ( pExtBlob == NULL )
{
return( EkuGetDecodedUsage(pPropBlob, pcbSize, pUsage) );
}
else if ( pPropBlob == NULL )
{
if ( fExtCertPolicies )
{
return( EkuDecodeCertPoliciesAndConvertToUsage(
pExtBlob, pcbSize, pUsage) );
}
else
{
return( EkuGetDecodedUsage(pExtBlob, pcbSize, pUsage) );
}
}
//
// Get the sizes we will need to allocate for decoding and validate
// the total against what was passed in
//
hr = EkuGetDecodedUsageSizes(
fExtCertPolicies,
pExtBlob,
pPropBlob,
&cbMergedSize,
&cbExtSize,
&cbPropSize
);
if ( hr != S_OK )
{
return( hr );
}
else if ( *pcbSize < cbMergedSize )
{
*pcbSize = cbMergedSize;
return( ERROR_MORE_DATA );
}
//
// Allocate the enhanced key usage structures and decode into them
//
pExtUsage = (PCERT_ENHKEY_USAGE)new BYTE [cbExtSize];
pPropUsage = (PCERT_ENHKEY_USAGE)new BYTE [cbPropSize];
if ( ( pExtUsage == NULL ) || ( pPropUsage == NULL ) )
{
delete pExtUsage;
delete pPropUsage;
return( E_OUTOFMEMORY );
}
if ( fExtCertPolicies )
{
hr = EkuDecodeCertPoliciesAndConvertToUsage(
pExtBlob, &cbExtSize, pExtUsage);
}
else
{
hr = EkuGetDecodedUsage(pExtBlob, &cbExtSize, pExtUsage);
}
if ( hr == S_OK )
{
hr = EkuGetDecodedUsage(pPropBlob, &cbPropSize, pPropUsage);
}
//
// Merge the usage structures
//
if ( hr == S_OK )
{
hr = EkuMergeUsage(
cbExtSize,
pExtUsage,
cbPropSize,
pPropUsage,
*pcbSize,
pUsage
);
}
//
// Cleanup
//
delete pExtUsage;
delete pPropUsage;
return( hr );
}
//+---------------------------------------------------------------------------
//
// Function: EkuEncodeUsage
//
// Synopsis: encodes the enhanced key usage into a blob useful for setting
// as a certificate property
//
//----------------------------------------------------------------------------
HRESULT EkuEncodeUsage (
PCERT_ENHKEY_USAGE pUsage,
PCRYPT_OBJID_BLOB pEkuBlob
)
{
HRESULT hr = S_OK;
DWORD cbData = 0;
LPBYTE pbData;
if ( CryptEncodeObject(
X509_ASN_ENCODING,
szOID_ENHANCED_KEY_USAGE,
pUsage,
NULL,
&cbData
) == FALSE )
{
return( GetLastError() );
}
pbData = new BYTE [cbData];
if ( pbData != NULL )
{
if ( CryptEncodeObject(
X509_ASN_ENCODING,
szOID_ENHANCED_KEY_USAGE,
pUsage,
pbData,
&cbData
) == FALSE )
{
hr = GetLastError();
}
}
else
{
hr = E_OUTOFMEMORY;
}
if ( hr == S_OK )
{
pEkuBlob->cbData = cbData;
pEkuBlob->pbData = pbData;
}
else
{
delete pbData;
}
return( hr );
}
//+---------------------------------------------------------------------------
//
// Function: EkuGetUsage
//
// Synopsis: gets the usage based on the flags with CertGetEnhancedKeyUsage
//
//----------------------------------------------------------------------------
HRESULT EkuGetUsage (
PCCERT_CONTEXT pCertContext,
DWORD dwFlags,
DWORD* pcbSize,
PCERT_ENHKEY_USAGE* ppUsage
)
{
DWORD cbSize;
PCERT_ENHKEY_USAGE pUsage;
//
// Get an appropriately sized block to hold the usage
//
if ( CertGetEnhancedKeyUsage(
pCertContext,
dwFlags,
NULL,
&cbSize
) == FALSE )
{
return( GetLastError() );
}
pUsage = (PCERT_ENHKEY_USAGE)new BYTE [cbSize];
if ( pUsage == NULL )
{
return( E_OUTOFMEMORY );
}
//
// Now get the enhanced key usage data and fill in the out parameters
//
if ( CertGetEnhancedKeyUsage(
pCertContext,
dwFlags,
pUsage,
&cbSize
) == FALSE )
{
delete pUsage;
return( GetLastError() );
}
if ( pcbSize != NULL )
{
*pcbSize = cbSize;
}
*ppUsage = pUsage;
return( S_OK );
}
//////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////
static BOOL OIDInUsages(PCERT_ENHKEY_USAGE pUsage, LPCSTR pszOID)
{
DWORD i;
// check every extension
for(i=0; i<pUsage->cUsageIdentifier; i++)
{
if(!strcmp(pUsage->rgpszUsageIdentifier[i], pszOID))
break;
}
return (i < pUsage->cUsageIdentifier);
}
//////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////
static BOOL OIDExistsInArray(LPSTR *rghPropOIDs, DWORD cPropOIDs, LPSTR pszOID)
{
DWORD i;
// check every extension
for(i=0; i<cPropOIDs; i++)
{
if(!strcmp(rghPropOIDs[i], pszOID))
break;
}
return (i < cPropOIDs);
}
//////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////
static LPSTR AllocAndCopyStr(LPSTR psz)
{
LPSTR pszNew;
pszNew = (LPSTR) new BYTE[strlen(psz)+1];
if (pszNew == NULL)
{
SetLastError((DWORD) E_OUTOFMEMORY);
return NULL;
}
strcpy(pszNew, psz);
return (pszNew);
}
//////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////
static void IntersectUsages(DWORD *pcExtOIDs, LPSTR *rghExtOIDs, PCERT_ENHKEY_USAGE pUsageExt)
{
DWORD i;
DWORD dwNumOIDs;
dwNumOIDs = *pcExtOIDs;
*pcExtOIDs = 0;
for (i=0; i<dwNumOIDs; i++)
{
if (OIDInUsages(pUsageExt, rghExtOIDs[i]))
{
if (*pcExtOIDs != i)
{
rghExtOIDs[*pcExtOIDs] = rghExtOIDs[i];
rghExtOIDs[i] = NULL;
}
(*pcExtOIDs)++;
}
else
{
delete(rghExtOIDs[i]);
rghExtOIDs[i] = NULL;
}
}
}
//////////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////////
static BOOL ProcessCertForEKU(
PCCERT_CONTEXT pCert,
BOOL *pfAllProp,
DWORD *pcPropOIDs,
LPSTR *rghPropOIDs,
BOOL *pfAllExt,
DWORD *pcExtOIDs,
LPSTR *rghExtOIDs)
{
BOOL fRet = TRUE;
PCERT_ENHKEY_USAGE pExtUsage = NULL;
PCERT_ENHKEY_USAGE pPropUsage = NULL;
DWORD i;
EkuGetUsage(pCert, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, NULL, &pExtUsage);
EkuGetUsage(pCert, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, NULL, &pPropUsage);
//
// if there are EKU extensions then we are limited to that set of EKUs at the maximum
//
if (pExtUsage != NULL)
{
//
// if this is the first cert with extensions then just copy all the EKUs,
// otherwise take the intersection of the current certs EKUs and the intersection
// of all the previous certs EKUs
//
if (*pfAllExt == TRUE)
{
*pfAllExt = FALSE;
for (i=0; i<pExtUsage->cUsageIdentifier; i++)
{
rghExtOIDs[i] = AllocAndCopyStr(pExtUsage->rgpszUsageIdentifier[i]);
if (rghExtOIDs[i] == NULL)
{
goto ErrorCleanUp;
}
(*pcExtOIDs)++;
}
}
else
{
IntersectUsages(pcExtOIDs, rghExtOIDs, pExtUsage);
}
}
//
// if there are EKU propertis then we are limited to that set of EKUs at the maximum
//
if (pPropUsage != NULL)
{
//
// if this is the first cert with extensions then just copy all the EKUs,
// otherwise take the intersection of the current certs EKUs and the intersection
// of all the previous certs EKUs
//
if (*pfAllProp == TRUE)
{
*pfAllProp = FALSE;
for (i=0; i<pPropUsage->cUsageIdentifier; i++)
{
rghPropOIDs[i] = AllocAndCopyStr(pPropUsage->rgpszUsageIdentifier[i]);
if (rghPropOIDs[i] == NULL)
{
goto ErrorCleanUp;
}
(*pcPropOIDs)++;
}
}
else
{
IntersectUsages(pcPropOIDs, rghPropOIDs, pPropUsage);
}
}
CleanUp:
if (pExtUsage != NULL)
delete(pExtUsage);
if (pPropUsage != NULL)
delete(pPropUsage);
return(fRet);
ErrorCleanUp:
fRet = FALSE;
goto CleanUp;
}
//+---------------------------------------------------------------------------
//
// Function: CertGetValidUsages
//
// Synopsis: takes an array of certs and returns an array of usages
// which consists of the intersection of the the valid usages for each cert.
// if each cert is good for all possible usages then cNumOIDs is set to -1.
//
//----------------------------------------------------------------------------
BOOL WINAPI CertGetValidUsages(
IN DWORD cCerts,
IN PCCERT_CONTEXT *rghCerts,
OUT int *cNumOIDs,
OUT LPSTR *rghOIDs,
IN OUT DWORD *pcbOIDs)
{
BOOL fAllExt = TRUE;
BOOL fAllProp = TRUE;
DWORD cPropOIDs = 0;
LPSTR rghPropOIDs[100];
DWORD cExtOIDs = 0;
LPSTR rghExtOIDs[100];
BOOL fRet = TRUE;
BYTE *pbBufferLocation;
DWORD cIntersectOIDs = 0;
DWORD i;
DWORD cbNeeded = 0;
for (i=0; i<cCerts; i++)
{
if (!ProcessCertForEKU(rghCerts[i], &fAllProp, &cPropOIDs, rghPropOIDs, &fAllExt, &cExtOIDs, rghExtOIDs))
{
goto ErrorCleanUp;
}
}
*cNumOIDs = 0;
if (fAllExt && fAllProp)
{
*pcbOIDs = 0;
*cNumOIDs = -1;
}
else if (!fAllExt && fAllProp)
{
for (i=0; i<cExtOIDs; i++)
{
cbNeeded += strlen(rghExtOIDs[i]) + 1 + sizeof(LPSTR);
(*cNumOIDs)++;
}
if (*pcbOIDs == 0)
{
*pcbOIDs = cbNeeded;
goto CleanUp;
}
if (cbNeeded > *pcbOIDs)
{
*pcbOIDs = cbNeeded;
SetLastError((DWORD) ERROR_MORE_DATA);
goto ErrorCleanUp;
}
pbBufferLocation = ((BYTE *)rghOIDs) + (cExtOIDs * sizeof(LPSTR));
for (i=0; i<cExtOIDs; i++)
{
rghOIDs[i] = (LPSTR) pbBufferLocation;
strcpy(rghOIDs[i], rghExtOIDs[i]);
pbBufferLocation += strlen(rghExtOIDs[i]) + 1;
}
}
else if (fAllExt && !fAllProp)
{
for (i=0; i<cPropOIDs; i++)
{
cbNeeded += strlen(rghPropOIDs[i]) + 1 + sizeof(LPSTR);
(*cNumOIDs)++;
}
if (*pcbOIDs == 0)
{
*pcbOIDs = cbNeeded;
goto CleanUp;
}
if (cbNeeded > *pcbOIDs)
{
*pcbOIDs = cbNeeded;
SetLastError((DWORD) ERROR_MORE_DATA);
goto ErrorCleanUp;
}
pbBufferLocation = ((BYTE *)rghOIDs) + (cPropOIDs * sizeof(LPSTR));
for (i=0; i<cPropOIDs; i++)
{
rghOIDs[i] = (LPSTR) pbBufferLocation;
strcpy(rghOIDs[i], rghPropOIDs[i]);
pbBufferLocation += strlen(rghPropOIDs[i]) + 1;
}
}
else
{
for (i=0; i<cExtOIDs; i++)
{
if (OIDExistsInArray(rghPropOIDs, cPropOIDs, rghExtOIDs[i]))
{
cbNeeded += strlen(rghExtOIDs[i]) + 1 + sizeof(LPSTR);
(*cNumOIDs)++;
cIntersectOIDs++;
}
}
if (*pcbOIDs == 0)
{
*pcbOIDs = cbNeeded;
goto CleanUp;
}
if (cbNeeded > *pcbOIDs)
{
*pcbOIDs = cbNeeded;
SetLastError((DWORD) ERROR_MORE_DATA);
goto ErrorCleanUp;
}
pbBufferLocation = ((BYTE *)rghOIDs) + (cIntersectOIDs * sizeof(LPSTR));
for (i=0; i<cExtOIDs; i++)
{
if (OIDExistsInArray(rghPropOIDs, cPropOIDs, rghExtOIDs[i]))
{
cIntersectOIDs--;
rghOIDs[cIntersectOIDs] = (LPSTR) pbBufferLocation;
strcpy(rghOIDs[cIntersectOIDs], rghExtOIDs[i]);
pbBufferLocation += strlen(rghExtOIDs[i]) + 1;
}
}
}
CleanUp:
for (i=0; i<cExtOIDs; i++)
{
delete(rghExtOIDs[i]);
}
for (i=0; i<cPropOIDs; i++)
{
delete(rghPropOIDs[i]);
}
return (fRet);
ErrorCleanUp:
fRet = FALSE;
goto CleanUp;
}
//+---------------------------------------------------------------------------
//
// Function: EkuGetIntersectedUsageViaGetValidUsages
//
// Synopsis: get the intersected extension and property usages
//
//----------------------------------------------------------------------------
BOOL
EkuGetIntersectedUsageViaGetValidUsages (
PCCERT_CONTEXT pCertContext,
DWORD* pcbSize,
PCERT_ENHKEY_USAGE pUsage
)
{
BOOL fResult;
int cUsage = 0;
DWORD cbUsage = 0;
DWORD cbSize = 0;
fResult = CertGetValidUsages( 1, &pCertContext, &cUsage, NULL, &cbUsage );
if ( fResult == TRUE )
{
cbSize = cbUsage + sizeof( CERT_ENHKEY_USAGE );
if ( pUsage == NULL )
{
*pcbSize = cbSize;
return( TRUE );
}
else if ( ( pUsage != NULL ) && ( *pcbSize < cbSize ) )
{
*pcbSize = cbSize;
SetLastError( (DWORD) ERROR_MORE_DATA );
return( FALSE );
}
pUsage->cUsageIdentifier = 0;
pUsage->rgpszUsageIdentifier = (LPSTR *)( (LPBYTE)pUsage + sizeof( CERT_ENHKEY_USAGE ) );
cbUsage = *pcbSize - sizeof( CERT_ENHKEY_USAGE );
fResult = CertGetValidUsages(
1,
&pCertContext,
(int *)&pUsage->cUsageIdentifier,
pUsage->rgpszUsageIdentifier,
&cbUsage
);
if ( fResult == TRUE )
{
if ( pUsage->cUsageIdentifier == 0xFFFFFFFF )
{
pUsage->cUsageIdentifier = 0;
SetLastError( (DWORD) CRYPT_E_NOT_FOUND );
}
else if ( pUsage->cUsageIdentifier == 0 )
{
SetLastError( 0 );
}
}
}
return( fResult );
}