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.
656 lines
17 KiB
656 lines
17 KiB
/*
|
|
** 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;
|
|
}
|