Windows2003-3790/inetcore/outlookexpress/msoert/capiutil.cpp
2020-09-30 16:53:55 +02:00

657 lines
17 KiB
C++

/*
** c a p i u t i l . c p p
**
** Purpose:
** A few helper functions for the crypt32 utilities
**
** History
** 5/22/97: (t-erikne) Created.
**
** Copyright (C) Microsoft Corp. 1997.
*/
/////////////////////////////////////////////////////////////////////////////
//
// Depends on
//
#include "pch.hxx"
#include "demand.h"
#include <BadStrFunctions.h>
///////////////// CAPI Enhancement code
LPVOID WINAPI CryptAllocFunc(size_t cbSize)
{
LPVOID pv;
if (!MemAlloc(&pv, cbSize)) {
return NULL;
}
return pv;
}
VOID WINAPI CryptFreeFunc(LPVOID pv)
{
MemFree(pv);
}
CRYPT_ENCODE_PARA CryptEncodeAlloc = {
sizeof(CRYPT_ENCODE_PARA), (PFN_CRYPT_ALLOC) CryptAllocFunc, CryptFreeFunc
};
CRYPT_DECODE_PARA CryptDecodeAlloc = {
sizeof(CRYPT_DECODE_PARA), (PFN_CRYPT_ALLOC) CryptAllocFunc, CryptFreeFunc
};
/* HrGetLastError
**
** Purpose:
** Convert a GetLastError value to an HRESULT
** A failure HRESULT must have the high bit set.
**
** Takes:
** none
**
** Returns:
** HRESULT
*/
HRESULT HrGetLastError(void)
{
DWORD error;
HRESULT hr;
error = GetLastError();
if (error && ! (error & 0x80000000)) {
hr = error | 0x80070000; // system error
} else {
hr = (HRESULT)error;
}
return(hr);
}
/* PVGetCertificateParam:
**
** Purpose:
** Combine the "how big? okay, here." double question to get a parameter
** from a certificate. Give it a thing to get and it will alloc the mem.
** Takes:
** IN pCert - CAPI certificate to query
** IN dwParam - parameter to find, ex: CERT_SHA1_HASH_PROP_ID
** OUT OPTIONAL cbOut - (def value of NULL) size of the returned PVOID
** Returns:
** data that was obtained, NULL if failed
*/
OESTDAPI_(LPVOID) PVGetCertificateParam(
PCCERT_CONTEXT pCert,
DWORD dwParam,
DWORD *cbOut)
{
HRESULT hr;
LPVOID pv;
hr = HrGetCertificateParam(pCert, dwParam, &pv, cbOut);
if (FAILED(hr)) {
return NULL;
}
return pv;
}
OESTDAPI_(HRESULT) HrGetCertificateParam(
PCCERT_CONTEXT pCert,
DWORD dwParam,
LPVOID * ppv,
DWORD *cbOut)
{
DWORD cbData;
BOOL f;
HRESULT hr = S_OK;
void * pvData = NULL;
if (!pCert)
{
hr = E_INVALIDARG;
goto ErrorReturn;
}
cbData = 0;
f = CertGetCertificateContextProperty(pCert, dwParam, NULL, &cbData);
if (!f || !cbData) {
hr = HrGetLastError();
goto ErrorReturn;
}
if (!MemAlloc(&pvData, cbData)) {
hr = E_OUTOFMEMORY;
goto ErrorReturn;
}
if (!CertGetCertificateContextProperty(pCert, dwParam, pvData, &cbData)) {
hr = HrGetLastError();
goto ErrorReturn;
}
*ppv = pvData;
exit:
if (cbOut)
*cbOut = cbData;
return hr;;
ErrorReturn:
if (pvData)
{
MemFree(pvData);
pvData = NULL;
}
cbData = 0;
goto exit;
}
/* HrDecodeObject:
**
** Purpose:
** Combine the "how big? okay, here." double question to decode an
** object. Give it a thing to get and it will alloc the mem. Return
** HRESULT to caller. Allow specification of decode flags.
** Takes:
** IN pbEncoded - encoded data
** IN cbEncoded - size of data in pbData
** IN item - X509_* ... the thing to get
** IN dwFlags - CRYPT_DECODE_NOCOPY_FLAG
** OUT OPTIONAL cbOut - (def value of NULL) size of the return
** OUT ppvOut - allocated buffer with return data
** Notes:
** pbEncoded can't be freed until return is freed if
** CRYPT_DECODE_NOCOPY_FLAG is specified.
** Returns:
** HRESULT
*/
OESTDAPI_(HRESULT) HrDecodeObject(
const BYTE *pbEncoded,
DWORD cbEncoded,
LPCSTR item,
DWORD dwFlags,
DWORD *pcbOut,
LPVOID *ppvOut)
{
DWORD cbData;
HRESULT hr = S_OK;
if (!(pbEncoded && cbEncoded && ppvOut))
{
hr = E_INVALIDARG;
goto ErrorReturn;
}
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, item, pbEncoded, cbEncoded,
dwFlags | CRYPT_DECODE_ALLOC_FLAG,
&CryptDecodeAlloc, ppvOut, &cbData)) {
hr = HrGetLastError();
}
else {
if (pcbOut != NULL) {
*pcbOut = cbData;
}
}
ErrorReturn:
return hr;
}
/* PVDecodeObject:
**
** Purpose:
** Combine the "how big? okay, here." double question to decode an
** object. Give it a thing to get and it will alloc the mem.
** Takes:
** IN pbEncoded - encoded data
** IN cbEncoded - size of data in pbData
** IN item - X509_* ... the thing to get
** OUT OPTIONAL cbOut - (def value of NULL) size of the return
** Notes:
** pbEncoded can't be freed until return is freed.
** Returns:
** data that was obtained, NULL if failed
*/
OESTDAPI_(LPVOID) PVDecodeObject(
const BYTE *pbEncoded,
DWORD cbEncoded,
LPCSTR item,
DWORD *pcbOut)
{
void *pvData = NULL;
HRESULT hr;
if (hr = HrDecodeObject(pbEncoded, cbEncoded, item, CRYPT_DECODE_NOCOPY_FLAG, pcbOut, &pvData)) {
SetLastError(hr);
}
return pvData;
}
/* SzGetAltNameEmail:
**
** Input:
** pCert -> certificate context
** lpszOID -> OID or predefined id of alt name to look in. ie, OID_SUBJECT_ALT_NAME or
** X509_ALTERNATE_NAME.
**
** Returns:
** Buffer containing email name or NULL if not found.
** Caller must MemFree the buffer.
*/
OESTDAPI_(LPSTR) SzGetAltNameEmail(
const PCCERT_CONTEXT pCert,
LPSTR lpszOID) {
PCERT_INFO pCertInfo = pCert->pCertInfo;
PCERT_ALT_NAME_ENTRY pAltNameEntry;
PCERT_ALT_NAME_INFO pAltNameInfo;
ULONG i, j, cbData;
LPSTR szRet = NULL;
if (lpszOID == (LPCSTR)X509_ALTERNATE_NAME) {
lpszOID = szOID_SUBJECT_ALT_NAME;
}
for (i = 0; i < pCertInfo->cExtension; i++) {
if (! lstrcmp(pCertInfo->rgExtension[i].pszObjId, lpszOID)) {
// Found the OID. Look for the email tag
if (pAltNameInfo = (PCERT_ALT_NAME_INFO)PVDecodeObject(
pCertInfo->rgExtension[i].Value.pbData,
pCertInfo->rgExtension[i].Value.cbData,
lpszOID,
NULL)) {
// Cycle through the alt name entries
for (j = 0; j < pAltNameInfo->cAltEntry; j++) {
if (pAltNameEntry = &pAltNameInfo->rgAltEntry[j]) {
if (pAltNameEntry->dwAltNameChoice == CERT_ALT_NAME_RFC822_NAME) {
// This is it, copy it out to a new allocation
if (pAltNameEntry->pwszRfc822Name)
{
cbData = WideCharToMultiByte(
CP_ACP,
0,
(LPWSTR)pAltNameEntry->pwszRfc822Name,
-1,
NULL,
0,
NULL,
NULL);
if (MemAlloc((LPVOID*)&szRet, cbData)) {
WideCharToMultiByte(
CP_ACP,
0,
(LPWSTR)pAltNameEntry->pwszRfc822Name,
-1,
szRet,
cbData,
NULL,
NULL);
return(szRet);
}
}
}
}
}
MemFree(pAltNameInfo);
}
}
}
return(NULL);
}
/* SzConvertRDNString
**
** Purpose:
** Figure out what kind of string data is in the RDN, allocate
** a buffer and convert the string data to DBCS/ANSI.
**
** Takes:
** IN pRdnAttr - Certificate RDN atteribute
** Returns:
** A MemAlloc'd buffer containing the string.
**
** BUGBUG: Should make mailnews use this function rather than
** rolling it's own.
*/
LPTSTR SzConvertRDNString(PCERT_RDN_ATTR pRdnAttr) {
LPTSTR szRet = NULL;
ULONG cbData = 0;
// We only handle certain types
//N look to see if we should have a stack var for the ->
if ((CERT_RDN_NUMERIC_STRING != pRdnAttr->dwValueType) &&
(CERT_RDN_PRINTABLE_STRING != pRdnAttr->dwValueType) &&
(CERT_RDN_IA5_STRING != pRdnAttr->dwValueType) &&
(CERT_RDN_VISIBLE_STRING != pRdnAttr->dwValueType) &&
(CERT_RDN_ISO646_STRING != pRdnAttr->dwValueType) &&
(CERT_RDN_UNIVERSAL_STRING != pRdnAttr->dwValueType) &&
(CERT_RDN_TELETEX_STRING != pRdnAttr->dwValueType) &&
(CERT_RDN_UNICODE_STRING != pRdnAttr->dwValueType)) {
Assert((CERT_RDN_NUMERIC_STRING == pRdnAttr->dwValueType) ||
(CERT_RDN_PRINTABLE_STRING == pRdnAttr->dwValueType) ||
(CERT_RDN_IA5_STRING == pRdnAttr->dwValueType) ||
(CERT_RDN_VISIBLE_STRING == pRdnAttr->dwValueType) ||
(CERT_RDN_ISO646_STRING == pRdnAttr->dwValueType) ||
(CERT_RDN_UNIVERSAL_STRING == pRdnAttr->dwValueType) ||
(CERT_RDN_TELETEX_STRING == pRdnAttr->dwValueType) ||
(CERT_RDN_UNICODE_STRING == pRdnAttr->dwValueType));
return(NULL);
}
// Find out how much space to allocate.
switch (pRdnAttr->dwValueType) {
case CERT_RDN_UNICODE_STRING:
cbData = WideCharToMultiByte(
CP_ACP,
0,
(LPWSTR)pRdnAttr->Value.pbData,
-1,
NULL,
0,
NULL,
NULL);
break;
case CERT_RDN_UNIVERSAL_STRING:
case CERT_RDN_TELETEX_STRING:
cbData = CertRDNValueToStr(pRdnAttr->dwValueType,
(PCERT_RDN_VALUE_BLOB)&(pRdnAttr->Value),
NULL,
0);
break;
default:
cbData = pRdnAttr->Value.cbData + 1;
break;
}
if (! MemAlloc((LPVOID*)&szRet, cbData)) {
Assert(szRet);
return(NULL);
}
// Copy the string
switch (pRdnAttr->dwValueType) {
case CERT_RDN_UNICODE_STRING:
if (FALSE == WideCharToMultiByte(
CP_ACP,
0,
(LPWSTR)pRdnAttr->Value.pbData,
-1,
szRet,
cbData,
NULL,
NULL)) {
LocalFree(szRet);
return(NULL);
}
break;
case CERT_RDN_UNIVERSAL_STRING:
case CERT_RDN_TELETEX_STRING:
CertRDNValueToStr(pRdnAttr->dwValueType,
(PCERT_RDN_VALUE_BLOB)&(pRdnAttr->Value),
szRet,
cbData);
break;
default:
StrCpyN(szRet, (LPCSTR)pRdnAttr->Value.pbData, cbData);
if (cbData)
{
szRet[cbData - 1] = '\0';
}
break;
}
return(szRet);
}
/* SzGetCertificateEmailAddress:
**
** Returns:
** NULL if there is no email address
*/
OESTDAPI_(LPSTR) SzGetCertificateEmailAddress(
const PCCERT_CONTEXT pCert)
{
PCERT_NAME_INFO pNameInfo;
PCERT_ALT_NAME_INFO pAltNameInfo = NULL;
PCERT_RDN_ATTR pRDNAttr;
LPSTR szRet = NULL;
Assert(pCert && pCert->pCertInfo);
if (pCert && pCert->pCertInfo)
{
pNameInfo = (PCERT_NAME_INFO)PVDecodeObject(pCert->pCertInfo->Subject.pbData,
pCert->pCertInfo->Subject.cbData, X509_NAME, 0);
if (pNameInfo)
{
pRDNAttr = CertFindRDNAttr(szOID_RSA_emailAddr, pNameInfo);
if (pRDNAttr)
{
Assert(0 == lstrcmp(szOID_RSA_emailAddr, pRDNAttr->pszObjId));
szRet = SzConvertRDNString(pRDNAttr);
}
MemFree(pNameInfo);
}
if (! szRet)
{
if (! (szRet = SzGetAltNameEmail(pCert, szOID_SUBJECT_ALT_NAME)))
{
szRet = SzGetAltNameEmail(pCert, szOID_SUBJECT_ALT_NAME2);
}
}
}
return(szRet);
}
/* PVGetMsgParam:
**
** Purpose:
** Combine the "how big? okay, here." double question to grab
** stuff from a message.
** Give it a thing to get and it will alloc the mem.
** Takes:
** IN hCryptMsg - message to query
** IN dwParam - CMSG_*
** IN dwIndex - depends on CMSG
** OUT OPTIONAL pcbOut - (def value of NULL) size of the return
** Returns:
** data that was obtained, NULL if failed
*/
OESTDAPI_(LPVOID) PVGetMsgParam(
HCRYPTMSG hCryptMsg,
DWORD dwParam,
DWORD dwIndex,
DWORD *pcbData)
{
HRESULT hr;
LPVOID pv;
hr = HrGetMsgParam(hCryptMsg, dwParam, dwIndex, &pv, pcbData);
if (FAILED(hr)) {
SetLastError(hr);
pv = NULL;
}
return pv;
}
OESTDAPI_(HRESULT) HrGetMsgParam(
HCRYPTMSG hCryptMsg,
DWORD dwParam,
DWORD dwIndex,
LPVOID * ppv,
DWORD *pcbData)
{
DWORD cbData;
BOOL f;
HRESULT hr = 0;
void * pvData = NULL;
if (!(hCryptMsg))
{
hr = E_INVALIDARG;
goto ErrorReturn;
}
cbData = 0;
f = CryptMsgGetParam(hCryptMsg, dwParam, dwIndex, NULL, &cbData);
if (!f || !cbData) {
hr = HrGetLastError();
goto ErrorReturn;
}
if (!MemAlloc(&pvData, cbData)) {
hr = E_OUTOFMEMORY;
goto ErrorReturn;
}
if (!CryptMsgGetParam(hCryptMsg, dwParam, dwIndex, pvData, &cbData)) {
hr = HrGetLastError();
goto ErrorReturn;
}
*ppv = pvData;
exit:
if (pcbData)
*pcbData = cbData;
return hr;
ErrorReturn:
if (pvData)
{
MemFree(pvData);
pvData = NULL;
}
cbData = 0;
goto exit;
}
//
// This function gets the usage bits of a certificate
// only the first 32 bits are retrieve, this is enough in most cases
//
HRESULT HrGetCertKeyUsage(PCCERT_CONTEXT pccert, DWORD * pdwUsage)
{
HRESULT hr = S_OK;
PCRYPT_BIT_BLOB pbits = NULL;
PCERT_EXTENSION pext;
DWORD cbStruct;
Assert(pccert != NULL && pdwUsage != NULL);
*pdwUsage = 0;
pext = CertFindExtension(szOID_KEY_USAGE,
pccert->pCertInfo->cExtension,
pccert->pCertInfo->rgExtension);
if(pext == NULL) {
//
// We do not have the intended key usage specified in the cert, we assume it
// is OK for all purpose initially.
//
*pdwUsage = 0xff;
goto ExitHere;
}
if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_KEY_USAGE,
pext->Value.pbData, pext->Value.cbData,
CRYPT_DECODE_ALLOC_FLAG, &CryptDecodeAlloc,
(void **)&pbits, &cbStruct))
{
Assert(pbits->cbData >= 1);
*pdwUsage = *pbits->pbData;
}
else
hr = HrGetLastError();
ExitHere:
if (pbits) {
CryptFreeFunc(pbits);
}
return hr;
}
// HrVerifyCertEnhKeyUsage
//
// This function verifies that the given certificate is valid for the
// E-MAIL purpose.
//
HRESULT HrVerifyCertEnhKeyUsage(PCCERT_CONTEXT pccert, LPCSTR szOID)
{
DWORD cb;
HRESULT hr;
HRESULT hrRet = S_FALSE;
DWORD i;
PCERT_EXTENSION pextEnhKeyUsage;
PCERT_ENHKEY_USAGE pusage = NULL;
// Check for the enhanced key usage extension
//
// Must have a correct enhanced key usage to be viable.
//
// Crack the usage on the cert
BOOL f = CertGetEnhancedKeyUsage(pccert, 0, NULL, &cb);
if (!f || (cb == 0))
{
hrRet = S_OK;
goto Exit;
}
if (!MemAlloc((LPVOID *) &pusage, cb))
{
hrRet = HrGetLastError();
goto Exit;
}
if (!CertGetEnhancedKeyUsage(pccert, 0, pusage, &cb))
{
// Bail and prevent the user from using this cert if we have
// any problems
hrRet = HrGetLastError();
goto Exit;
}
// Make sure that this certificate is valid for E-Mail purposes
for (i = 0; i < pusage->cUsageIdentifier; i++)
if (0 == strcmp(pusage->rgpszUsageIdentifier[i], szOID))
hrRet = S_OK;
Exit:
SafeMemFree(pusage);
return hrRet;
}