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.
 
 
 
 
 
 

4112 lines
92 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1995 - 1999
//
// File: admin.cpp
//
//--------------------------------------------------------------------------
#include <pch.cpp>
#pragma hdrstop
#include <setupapi.h>
#include <ocmanage.h>
#include "initcert.h"
#include "cscsp.h"
#define __dwFILE__ __dwFILE_CERTUTIL_ADMIN_CPP__
#define wszV1SUFFIX L"-v1"
#define wszP12SUFFIX L".p12"
#define wszRECSUFFIX L".rec"
#define wszEPFSUFFIX L".epf"
HRESULT
verbDenyRequest(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszRequestId,
IN WCHAR const *pwszArg2,
IN WCHAR const *pwszArg3,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
DISPATCHINTERFACE diAdmin;
LONG RequestId;
BOOL fMustRelease = FALSE;
hr = myGetLong(pwszRequestId, &RequestId);
_JumpIfError(hr, error, "RequestId must be a number");
hr = Admin_Init(g_DispatchFlags, &diAdmin);
_JumpIfError(hr, error, "Admin_Init");
fMustRelease = TRUE;
hr = Admin_DenyRequest(&diAdmin, g_pwszConfig, RequestId);
_JumpIfError(hr, error, "Admin_DenyRequest");
error:
if (fMustRelease)
{
Admin_Release(&diAdmin);
}
return(hr);
}
WCHAR const *
wszFromSubmitDisposition(
LONG Disposition)
{
DWORD idMsg;
switch (Disposition)
{
case CR_DISP_INCOMPLETE: idMsg = IDS_CR_DISP_INCOMPLETE; break;
case CR_DISP_ERROR: idMsg = IDS_CR_DISP_ERROR; break;
case CR_DISP_DENIED: idMsg = IDS_CR_DISP_DENIED; break;
case CR_DISP_ISSUED: idMsg = IDS_CR_DISP_ISSUED; break;
case CR_DISP_ISSUED_OUT_OF_BAND:
idMsg = IDS_CR_DISP_ISSUED_OUT_OF_BAND; break;
case CR_DISP_UNDER_SUBMISSION:
idMsg = IDS_CR_DISP_UNDER_SUBMISSION; break;
case CR_DISP_REVOKED: idMsg = IDS_CR_DISP_REVOKED; break;
default: idMsg = IDS_UNKNOWN; break;
}
return(myLoadResourceString(idMsg));
}
HRESULT
verbResubmitRequest(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszRequestId,
IN WCHAR const *pwszArg2,
IN WCHAR const *pwszArg3,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
DISPATCHINTERFACE diAdmin;
LONG RequestId;
LONG Disposition;
BOOL fMustRelease = FALSE;
hr = myGetLong(pwszRequestId, &RequestId);
_JumpIfError(hr, error, "RequestId must be a number");
hr = Admin_Init(g_DispatchFlags, &diAdmin);
_JumpIfError(hr, error, "Admin_Init");
fMustRelease = TRUE;
hr = Admin_ResubmitRequest(&diAdmin, g_pwszConfig, RequestId, &Disposition);
_JumpIfError(hr, error, "Admin_ResubmitRequest");
if (CR_DISP_UNDER_SUBMISSION == Disposition)
{
wprintf(
myLoadResourceString(IDS_FORMAT_PENDING_REQUESTID), // "Certificate request is pending: RequestId: %u"
RequestId);
wprintf(wszNewLine);
}
else if (CR_DISP_ISSUED == Disposition)
{
wprintf(myLoadResourceString(IDS_CERT_ISSUED)); // "Certificate issued."
wprintf(wszNewLine);
}
else
{
if (FAILED(Disposition))
{
hr = Disposition;
Disposition = CR_DISP_DENIED;
}
wprintf(
myLoadResourceString(IDS_CERT_NOT_ISSUED_DISPOSITION), // "Certificate has not been issued: Disposition: %d -- %ws"
Disposition,
wszFromSubmitDisposition(Disposition));
wprintf(wszNewLine);
if (S_OK != hr)
{
WCHAR const *pwszMessage;
pwszMessage = myGetErrorMessageText(hr, FALSE);
if (NULL != pwszMessage)
{
wprintf(L"%ws\n", pwszMessage);
LocalFree(const_cast<WCHAR *>(pwszMessage));
}
}
}
error:
if (fMustRelease)
{
Admin_Release(&diAdmin);
}
return(hr);
}
typedef struct _cuCRLREASON
{
WCHAR *pwszReason;
LONG lReason;
int idReason;
} cuCRLREASON;
#define cuREASON(r, id) { L#r, (r), (id) }
cuCRLREASON g_cuReason[] =
{
cuREASON(CRL_REASON_UNSPECIFIED, IDS_CRL_REASON_UNSPECIFIED),
cuREASON(CRL_REASON_KEY_COMPROMISE, IDS_CRL_REASON_KEY_COMPROMISE),
cuREASON(CRL_REASON_CA_COMPROMISE, IDS_CRL_REASON_CA_COMPROMISE),
cuREASON(CRL_REASON_AFFILIATION_CHANGED, IDS_CRL_REASON_AFFILIATION_CHANGED),
cuREASON(CRL_REASON_SUPERSEDED, IDS_CRL_REASON_SUPERSEDED),
cuREASON(CRL_REASON_CESSATION_OF_OPERATION,
IDS_CRL_REASON_CESSATION_OF_OPERATION),
cuREASON(CRL_REASON_CERTIFICATE_HOLD, IDS_CRL_REASON_CERTIFICATE_HOLD),
cuREASON(CRL_REASON_REMOVE_FROM_CRL, IDS_CRL_REASON_REMOVE_FROM_CRL),
{ L"Unrevoke", MAXDWORD, IDS_CRL_REASON_UNREVOKE },
{ NULL, MAXDWORD, IDS_CRL_REASON_UNRECOGNIZED },
};
#define wszCRLPREFIX L"CRL_REASON_"
HRESULT
cuParseReason(
IN WCHAR const *pwszReason,
OUT LONG *plReason)
{
HRESULT hr;
hr = myGetSignedLong(pwszReason, plReason);
if (S_OK != hr)
{
cuCRLREASON const *pr;
for (pr = g_cuReason; ; pr++)
{
if (NULL == pr->pwszReason)
{
hr = E_INVALIDARG;
_JumpIfError(hr, error, "Invalid Reason string");
}
if (0 == mylstrcmpiS(pr->pwszReason, pwszReason))
{
break;
}
if (wcslen(pr->pwszReason) > WSZARRAYSIZE(wszCRLPREFIX) &&
0 == memcmp(
pr->pwszReason,
wszCRLPREFIX,
WSZARRAYSIZE(wszCRLPREFIX) * sizeof(WCHAR)) &&
0 == LSTRCMPIS(
pwszReason,
&pr->pwszReason[WSZARRAYSIZE(wszCRLPREFIX)]))
{
break;
}
}
*plReason = pr->lReason;
hr = S_OK;
}
CSASSERT(S_OK == hr);
error:
return(hr);
}
int
cuidCRLReason(
IN LONG Reason)
{
cuCRLREASON const *pr;
for (pr = g_cuReason; NULL != pr->pwszReason; pr++)
{
if (pr->lReason == Reason)
{
break;
}
}
return(pr->idReason);
}
WCHAR const *
wszCRLReason(
IN LONG Reason)
{
return(myLoadResourceString(cuidCRLReason(Reason)));
}
HRESULT
verbRevokeCertificate(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszSerialNumberList,
IN WCHAR const *pwszReason,
IN WCHAR const *pwszArg3,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
HRESULT hr2;
DISPATCHINTERFACE diAdmin;
WCHAR **ppwszSerialList = NULL;
BSTR strSerialNumber = NULL;
LONG Reason = CRL_REASON_UNSPECIFIED;
SYSTEMTIME st;
FILETIME ft;
DATE Date;
BOOL fMustRelease = FALSE;
GetSystemTime(&st);
if (!SystemTimeToFileTime(&st, &ft))
{
hr = myHLastError();
_JumpIfError(hr, error, "SystemTimeToFileTime");
}
hr = myFileTimeToDate(&ft, &Date);
_JumpIfError(hr, error, "myFileTimeToDate");
//Date -= 1.0; // Revoke effective yesterday
if (NULL != pwszReason)
{
hr = cuParseReason(pwszReason, &Reason);
_JumpIfError(hr, error, "Invalid Reason");
}
hr = cuParseStrings(
pwszSerialNumberList,
FALSE,
NULL,
NULL,
&ppwszSerialList,
NULL);
_JumpIfError(hr, error, "cuParseStrings");
hr = Admin_Init(g_DispatchFlags, &diAdmin);
_JumpIfError(hr, error, "Admin_Init");
fMustRelease = TRUE;
if (NULL != ppwszSerialList)
{
DWORD i;
for (i = 0; NULL != ppwszSerialList[i]; i++)
{
hr2 = myMakeSerialBstr(ppwszSerialList[i], &strSerialNumber);
if (S_OK == hr)
{
hr = hr2;
}
_JumpIfError(hr2, error, "myMakeSerialBstr");
wprintf(myLoadResourceString(IDS_REVOKING), strSerialNumber); // "Revoking "%ws""
wprintf(L" -- %ws", wszCRLReason(Reason)); // "Reason: xxxx"
wprintf(wszNewLine);
hr2 = Admin_RevokeCertificate(
&diAdmin,
g_pwszConfig,
strSerialNumber,
Reason,
Date);
if (S_OK != hr2)
{
cuPrintAPIError(L"ICertAdmin::RevokeCertificate", hr2);
_PrintError(hr2, "Admin_RevokeCertificate");
if (S_OK == hr)
{
hr = hr2;
}
}
SysFreeString(strSerialNumber);
strSerialNumber = NULL;
}
}
_JumpIfError(hr, error, "Admin_RevokeCertificate");
error:
cuFreeStringArray(ppwszSerialList);
if (fMustRelease)
{
Admin_Release(&diAdmin);
}
if (NULL != strSerialNumber)
{
SysFreeString(strSerialNumber);
}
return(hr);
}
HRESULT
cuParseDaysHours(
IN WCHAR const *pwszDaysHours,
OUT FILETIME *pft)
{
HRESULT hr;
WCHAR *pwszDays = NULL;
WCHAR *pwszHours;
DWORD dwDays;
DWORD dwHours;
BOOL fValid;
LONGLONG delta;
hr = myDupString(pwszDaysHours, &pwszDays);
_JumpIfError(hr, error, "myDupString");
hr = E_INVALIDARG;
pwszHours = wcschr(pwszDays, L':');
if (NULL == pwszHours)
{
_JumpError(hr, error, "missing colon");
}
*pwszHours++ = L'\0';
dwDays = myWtoI(pwszDays, &fValid);
if (!fValid)
{
_JumpError(hr, error, "bad day count");
}
dwHours = myWtoI(pwszHours, &fValid);
if (!fValid)
{
_JumpError(hr, error, "bad hour count");
}
if (0 == dwDays && 0 == dwHours)
{
_JumpError(hr, error, "zero day+hour counts");
}
GetSystemTimeAsFileTime(pft);
// add specified days and hours to compute expiration date
delta = dwDays * CVT_DAYS;
delta += dwHours * CVT_HOURS;
myAddToFileTime(pft, delta * CVT_BASE);
hr = S_OK;
error:
if (NULL != pwszDays)
{
LocalFree(pwszDays);
}
return(hr);
}
HRESULT
verbPublishCRL(
IN WCHAR const *pwszOption,
OPTIONAL IN WCHAR const *pwszDaysHours,
OPTIONAL IN WCHAR const *pwszDelta,
IN WCHAR const *pwszArg3,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
DISPATCHINTERFACE diAdmin;
BOOL fMustRelease = FALSE;
DATE Date;
DWORD Flags = 0;
if (NULL != pwszDaysHours && 0 == LSTRCMPIS(pwszDaysHours, L"delta"))
{
WCHAR const *pwsz = pwszDaysHours;
pwszDaysHours = pwszDelta;
pwszDelta = pwsz;
}
Date = 0.0;
if (NULL != pwszDaysHours)
{
if (0 == LSTRCMPIS(pwszDaysHours, L"republish"))
{
Flags |= CA_CRL_REPUBLISH;
}
else
{
FILETIME ft;
hr = cuParseDaysHours(pwszDaysHours, &ft);
_JumpIfError(hr, error, "cuParseDaysHours");
hr = myFileTimeToDate(&ft, &Date);
_JumpIfError(hr, error, "myFileTimeToDate");
}
}
if (NULL != pwszDelta)
{
if (0 != LSTRCMPIS(pwszDelta, L"delta"))
{
hr = E_INVALIDARG;
_JumpError(hr, error, "bad delta arg");
}
Flags |= CA_CRL_DELTA;
}
if (0 == (CA_CRL_DELTA & Flags))
{
Flags |= CA_CRL_BASE;
}
hr = Admin_Init(g_DispatchFlags, &diAdmin);
_JumpIfError(hr, error, "Admin_Init");
fMustRelease = TRUE;
if ((CA_CRL_DELTA | CA_CRL_REPUBLISH) & Flags)
{
hr = Admin2_PublishCRLs(&diAdmin, g_pwszConfig, Date, Flags);
_JumpIfError(hr, error, "Admin2_PublishCRLs");
}
else
{
BOOL fV1 = g_fV1Interface;
if (!fV1)
{
hr = Admin2_PublishCRLs(&diAdmin, g_pwszConfig, Date, Flags);
if (E_NOTIMPL != hr && RPC_E_VERSION_MISMATCH != hr)
{
_JumpIfError(hr, error, "Admin2_PublishCRLs");
}
else
{
_PrintError(hr, "Admin2_PublishCRLs down level server");
fV1 = TRUE;
}
}
if (fV1)
{
hr = Admin_PublishCRL(&diAdmin, g_pwszConfig, Date);
_JumpIfError(hr, error, "Admin_PublishCRL");
}
}
error:
if (fMustRelease)
{
Admin_Release(&diAdmin);
}
return(hr);
}
HRESULT
verbGetCRL(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszfnOut,
OPTIONAL IN WCHAR const *pwszDelta,
OPTIONAL IN WCHAR const *pwszIndex,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
DISPATCHINTERFACE diAdmin;
BOOL fMustRelease = FALSE;
BOOL fDelta = FALSE;
DWORD Index = MAXDWORD; // default to latest CRL
BSTR strCRL = NULL;
if (NULL != pwszDelta && 0 != LSTRCMPIS(pwszDelta, L"delta"))
{
WCHAR const *pwsz = pwszIndex;
pwszIndex = pwszDelta;
pwszDelta = pwsz;
}
if (NULL != pwszDelta && 0 == LSTRCMPIS(pwszDelta, L"delta"))
{
fDelta = TRUE;
}
if (NULL != pwszIndex)
{
hr = myGetSignedLong(pwszIndex, (LONG *) &Index);
_JumpIfErrorStr(hr, error, "Cert index not a number", pwszIndex);
}
hr = Admin_Init(g_DispatchFlags, &diAdmin);
_JumpIfError(hr, error, "Admin_Init");
fMustRelease = TRUE;
if (fDelta)
{
hr = Admin2_GetCAProperty(
&diAdmin,
g_pwszConfig,
CR_PROP_DELTACRL,
Index,
PROPTYPE_BINARY,
CR_OUT_BINARY,
&strCRL);
_JumpIfError(hr, error, "Admin2_GetCAProperty");
}
else
{
BOOL fV1 = g_fV1Interface;
if (!fV1)
{
hr = Admin2_GetCAProperty(
&diAdmin,
g_pwszConfig,
CR_PROP_BASECRL,
Index,
PROPTYPE_BINARY,
CR_OUT_BINARY,
&strCRL);
if (E_NOTIMPL != hr && RPC_E_VERSION_MISMATCH != hr)
{
_JumpIfError(hr, error, "Admin2_GetCAProperty");
}
else
{
_PrintError(hr, "Admin2_CAProperty down level server");
fV1 = TRUE;
}
}
if (fV1)
{
if (NULL != pwszIndex)
{
hr = cuGetCAInfo(
pwszOption,
pwszfnOut,
g_wszCAInfoCRL,
pwszIndex);
_JumpIfError(hr, error, "cuGetCAInfo");
}
else
{
hr = Admin_GetCRL(
&diAdmin,
g_pwszConfig,
CR_OUT_BINARY,
&strCRL);
_JumpIfError(hr, error, "Admin_GetCRL");
}
}
}
// if not already saved by cuGetCAInfo
if (NULL != strCRL)
{
hr = EncodeToFileW(
pwszfnOut,
(BYTE const *) strCRL,
SysStringByteLen(strCRL),
CRYPT_STRING_BINARY | g_EncodeFlags);
_JumpIfError(hr, error, "EncodeToFileW");
}
error:
if (fMustRelease)
{
Admin_Release(&diAdmin);
}
if (NULL != strCRL)
{
SysFreeString(strCRL);
}
return(hr);
}
HRESULT
BuildDummyCert(
IN CERT_CONTEXT const *pCert,
IN WCHAR const *pwszSerialNumber,
IN CHAR const *pszObjId,
OUT BYTE **ppbCert,
OUT DWORD *pcbCert)
{
HRESULT hr;
CERT_INFO CertInfoOut;
CERT_INFO const *pCertInfo;
BYTE *pbUnsigned = NULL;
DWORD cbUnsigned;
CERT_CONTEXT CertContext;
WCHAR *pwszContainer = NULL;
HCRYPTPROV hProv = NULL;
CERT_PUBLIC_KEY_INFO *pPubKey = NULL;
DWORD cbPubKey;
CERT_EXTENSION extSKI = {szOID_SUBJECT_KEY_IDENTIFIER, FALSE, 0, NULL};
CERT_EXTENSION extAKI = {szOID_AUTHORITY_KEY_IDENTIFIER2, FALSE, 0, NULL};
CERT_EXTENSION aExt[2];
CERT_EXTENSION *pExt;
CRYPT_DATA_BLOB *pBlob = NULL;
DWORD cb;
*ppbCert = NULL;
ZeroMemory(&CertInfoOut, sizeof(CertInfoOut));
pCertInfo = pCert->pCertInfo;
CertInfoOut.Issuer = pCertInfo->Issuer;
CertInfoOut.NotBefore = pCertInfo->NotBefore;
CertInfoOut.NotAfter = pCertInfo->NotAfter;
CertInfoOut.SignatureAlgorithm.pszObjId = const_cast<CHAR *>(pszObjId);
hr = WszToMultiByteInteger(
FALSE,
pwszSerialNumber,
&CertInfoOut.SerialNumber.cbData,
&CertInfoOut.SerialNumber.pbData);
_JumpIfError(hr, error, "WszToMultiByteInteger");
hr = myCertStrToName(
X509_ASN_ENCODING,
L"CN=Dummy", // pszX500
CERT_NAME_STR_REVERSE_FLAG,
NULL, // pvReserved
&CertInfoOut.Subject.pbData,
&CertInfoOut.Subject.cbData,
NULL); // ppszError
_JumpIfError(hr, error, "myCertStrToName");
ZeroMemory(&CertContext, sizeof(CertContext));
CertContext.dwCertEncodingType = X509_ASN_ENCODING;
CertContext.pCertInfo = &CertInfoOut;
hr = cuGenerateKeyContainerName(&CertContext, &pwszContainer);
_JumpIfError(hr, error, "cuGenerateKeyContainerName");
hr = myGenerateKeys(
pwszContainer,
NULL, // pwszProvName
0, // dwFlags
FALSE, // fMachineKeySet
AT_SIGNATURE,
PROV_RSA_FULL,
0, // dwKeySize (use default)
&hProv);
_JumpIfError(hr, error, "myGenerateKeys");
if (!myCryptExportPublicKeyInfo(
hProv,
AT_SIGNATURE,
CERTLIB_USE_LOCALALLOC,
&pPubKey,
&cbPubKey))
{
hr = myHLastError();
_JumpError(hr, error, "myCryptExportPublicKeyInfo");
}
CertInfoOut.SubjectPublicKeyInfo = *pPubKey; // Structure assignment
// Subject Key Identifier extension:
hr = myCreateSubjectKeyIdentifierExtension(
pPubKey,
&extSKI.Value.pbData,
&extSKI.Value.cbData);
_JumpIfError(hr, error, "myCreateSubjectKeyIdentifierExtension");
CertInfoOut.rgExtension = aExt;
aExt[CertInfoOut.cExtension] = extSKI;
CertInfoOut.cExtension++;
//AKI extension?
pExt = CertFindExtension(
szOID_SUBJECT_KEY_IDENTIFIER,
pCertInfo->cExtension,
pCertInfo->rgExtension);
if (NULL != pExt)
{
CERT_AUTHORITY_KEY_ID2_INFO AKI;
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_OCTET_STRING,
pExt->Value.pbData,
pExt->Value.cbData,
CERTLIB_USE_LOCALALLOC,
(VOID **) &pBlob,
&cb))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
ZeroMemory(&AKI, sizeof(AKI));
AKI.KeyId = *pBlob;
if (!myEncodeKeyAuthority2(
X509_ASN_ENCODING,
&AKI,
CERTLIB_USE_LOCALALLOC,
&extAKI.Value.pbData,
&extAKI.Value.cbData))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeKeyAuthority2");
}
aExt[CertInfoOut.cExtension] = extAKI;
CertInfoOut.cExtension++;
}
CertInfoOut.dwVersion = CERT_V3;
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_CERT_TO_BE_SIGNED,
&CertInfoOut,
0,
CERTLIB_USE_LOCALALLOC,
&pbUnsigned, // pbEncoded
&cbUnsigned))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
*ppbCert = pbUnsigned;
pbUnsigned = NULL;
*pcbCert = cbUnsigned;
hr = S_OK;
error:
if (NULL != pbUnsigned)
{
LocalFree(pbUnsigned);
}
if (NULL != pBlob)
{
LocalFree(pBlob);
}
if (NULL != extSKI.Value.pbData)
{
LocalFree(extSKI.Value.pbData);
}
if (NULL != extAKI.Value.pbData)
{
LocalFree(extAKI.Value.pbData);
}
if (NULL != CertInfoOut.SerialNumber.pbData)
{
LocalFree(CertInfoOut.SerialNumber.pbData);
}
if (NULL != CertInfoOut.Subject.pbData)
{
LocalFree(CertInfoOut.Subject.pbData);
}
if (NULL != pPubKey)
{
LocalFree(pPubKey);
}
if (NULL != hProv)
{
CryptReleaseContext(hProv, 0);
if (NULL != pwszContainer)
{
CryptAcquireContext(
&hProv,
pwszContainer,
NULL, // pwszProvName
PROV_RSA_FULL,
CRYPT_DELETEKEYSET);
}
}
if (NULL != pwszContainer)
{
LocalFree(pwszContainer);
}
return(hr);
}
HRESULT
FindCertAndSign(
OPTIONAL IN CERT_EXTENSION const *pExtKeyId,
OPTIONAL IN BYTE const *pbHash,
IN DWORD cbHash,
OPTIONAL IN BYTE const *pbUnsigned,
IN DWORD cbUnsigned,
OPTIONAL IN WCHAR const *pwszSerialNumber,
OUT BYTE **ppbOut,
OUT DWORD *pcbOut)
{
HRESULT hr;
CERT_AUTHORITY_KEY_ID2_INFO *pKeyId = NULL;
DWORD cbKeyId;
BSTR strKeyId = NULL;
CERT_CONTEXT const *pCert = NULL;
HCRYPTPROV hProv = NULL;
DWORD dwKeySpec;
BOOL fCallerFreeProv;
CHAR *pszObjId = NULL;
BYTE *pbCert = NULL;
DWORD cbCert;
*ppbOut = NULL;
CSASSERT(NULL != pbUnsigned || NULL != pwszSerialNumber);
if (NULL == pbHash && NULL != pExtKeyId)
{
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_AUTHORITY_KEY_ID2,
pExtKeyId->Value.pbData,
pExtKeyId->Value.cbData,
CERTLIB_USE_LOCALALLOC,
(VOID **) &pKeyId,
&cbKeyId))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
pbHash = pKeyId->KeyId.pbData;
cbHash = pKeyId->KeyId.cbData;
}
if (0 != cbHash && NULL != pbHash)
{
hr = MultiByteIntegerToBstr(TRUE, cbHash, pbHash, &strKeyId);
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
}
// Find CA cert by KeyId from the szOID_AUTHORITY_KEY_IDENTIFIER2 extension.
// Look in HKLM and HKCU My and CA stores.
hr = myGetCertificateFromPicker(
g_hInstance,
NULL, // hwndParent
IDS_GETCERT_TITLE,
IDS_GETCERT_SUBTITLE,
// dwFlags: HKLM+HKCU My store
CUCS_MYSTORE |
CUCS_MACHINESTORE |
CUCS_USERSTORE |
CUCS_PRIVATEKEYREQUIRED |
CUCS_ARCHIVED |
(g_fCryptSilent? CUCS_SILENT : 0),
strKeyId,
0,
NULL,
0, // cpszObjId
NULL, // apszObjId
&pCert);
_JumpIfError(hr, error, "myGetCertificateFromPicker");
if (NULL == pCert)
{
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
_JumpError(hr, error, "no cert");
}
hr = cuDisplayCertName(
TRUE,
NULL,
myLoadResourceString(IDS_SIGNINGSUBJECT), // "Signing certificate Subject"
g_wszPad4,
&pCert->pCertInfo->Subject,
pCert->pCertInfo);
_JumpIfError(hr, error, "cuDisplayCertName(Subject)");
// Search for and load the cryptographic provider and private key.
hr = myLoadPrivateKey(
&pCert->pCertInfo->SubjectPublicKeyInfo,
CUCS_MACHINESTORE | CUCS_USERSTORE | CUCS_MYSTORE | CUCS_ARCHIVED,
&hProv,
&dwKeySpec,
&fCallerFreeProv);
_JumpIfError(hr, error, "myLoadPrivateKey");
if (AT_SIGNATURE != dwKeySpec)
{
hr = NTE_BAD_KEY_STATE;
DBGPRINT((DBG_SS_CERTUTIL, "dwKeySpec = %u\n", dwKeySpec));
_JumpError(hr, error, "dwKeySpec");
}
// The CA cert's private key is available -- use it to sign the data.
// Sign the Cert or CRL and encode the signed info.
hr = myGetSigningOID(hProv, NULL, 0, CALG_SHA1, &pszObjId);
_JumpIfError(hr, error, "myGetSigningOID");
if (NULL != pwszSerialNumber)
{
hr = BuildDummyCert(
pCert,
pwszSerialNumber,
pszObjId,
&pbCert,
&cbCert);
_JumpIfError(hr, error, "BuildDummyCert");
pbUnsigned = pbCert;
cbUnsigned = cbCert;
}
hr = myEncodeSignedContent(
hProv,
X509_ASN_ENCODING,
pszObjId,
const_cast<BYTE *>(pbUnsigned),
cbUnsigned,
CERTLIB_USE_LOCALALLOC,
ppbOut,
pcbOut);
_JumpIfError(hr, error, "myEncodeSignedContent");
error:
if (NULL != pbCert)
{
LocalFree(pbCert);
}
if (NULL != pszObjId)
{
LocalFree(pszObjId);
}
if (NULL != pKeyId)
{
LocalFree(pKeyId);
}
if (NULL != strKeyId)
{
SysFreeString(strKeyId);
}
if (NULL != pCert)
{
CertFreeCertificateContext(pCert);
}
if (NULL != hProv && fCallerFreeProv)
{
CryptReleaseContext(hProv, 0);
}
return(hr);
}
VOID
SetExpiration(
OPTIONAL IN FILETIME const *pftNotAfterNew,
IN OUT FILETIME *pftNotBefore,
OPTIONAL IN OUT FILETIME *pftNotAfter)
{
if (NULL == pftNotAfterNew ||
0 != pftNotAfterNew->dwLowDateTime ||
0 != pftNotAfterNew->dwHighDateTime)
{
LLFILETIME llftNotBefore;
LLFILETIME llft;
LLFILETIME llftDelta;
llftNotBefore.ft = *pftNotBefore; // Save orignal value
// current time - clock skew
GetSystemTimeAsFileTime(&llft.ft);
llftDelta.ll = CCLOCKSKEWMINUTESDEFAULT * CVT_MINUTES;
llftDelta.ll *= CVT_BASE;
llft.ll -= llftDelta.ll;
// NotBeforeOut = oldest of NotBefore, (CurrentTime - skew)
if (llftNotBefore.ll > llft.ll)
{
*pftNotBefore = llft.ft;
}
if (NULL != pftNotAfter)
{
LLFILETIME llftNotAfter;
llftNotAfter.ft = *pftNotAfter; // Save orignal value
if (NULL != pftNotAfterNew)
{
*pftNotAfter = *pftNotAfterNew;
}
else
{
// NotAfterOut = (CurrentTime - skew) + (NotAfter - NotBefore);
llft.ll += llftNotAfter.ll;
llft.ll -= llftNotBefore.ll;
*pftNotAfter = llft.ft;
}
}
}
}
HRESULT
RemoveExtensions(
IN WCHAR const * const *ppwszObjIdList,
IN BOOL fValidate,
IN DWORD cExtensionIn,
IN CERT_EXTENSION *rgExtensionIn,
OUT DWORD *pcExtensionOut,
OUT CERT_EXTENSION **prgExtensionOut)
{
HRESULT hr;
DWORD cExtension = cExtensionIn;
CERT_EXTENSION *rgExtension = NULL;
*prgExtensionOut = NULL;
rgExtension = (CERT_EXTENSION *) LocalAlloc(
LMEM_FIXED,
cExtension * sizeof(rgExtension[0]));
if (NULL == rgExtension)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
CopyMemory(
rgExtension,
rgExtensionIn,
cExtension * sizeof(rgExtension[0]));
if (NULL != ppwszObjIdList)
{
DWORD i;
for (i = 0; NULL != ppwszObjIdList[i]; i++)
{
WCHAR const *pwszObjId = ppwszObjIdList[i];
char *pszObjId;
CERT_EXTENSION *pExt;
hr = myVerifyObjId(pwszObjId);
if (S_OK != hr)
{
if (fValidate)
{
_JumpError(hr, error, "myVerifyObjId");
}
continue;
}
if (!myConvertWszToSz(&pszObjId, pwszObjId, -1))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "myConvertWszToSz");
}
pExt = CertFindExtension(pszObjId, cExtension, rgExtension);
if (NULL != pExt)
{
DWORD iDel = SAFE_SUBTRACT_POINTERS(pExt, rgExtension);
// wprintf(L"iDel=%u cExt=%u\n", iDel, cExtension);
if (iDel < cExtension)
{
// wprintf(L"copy %u to %u, len=%u\n", iDel + 1, iDel, cExtension - iDel - 1);
MoveMemory(
&rgExtension[iDel],
&rgExtension[iDel + 1],
(cExtension - iDel - 1) * sizeof(rgExtension[iDel]));
}
cExtension--;
}
LocalFree(pszObjId);
}
}
*pcExtensionOut = cExtension;
*prgExtensionOut = rgExtension;
rgExtension = NULL;
hr = S_OK;
error:
if (NULL != rgExtension)
{
LocalFree(rgExtension);
}
return(hr);
}
CRL_ENTRY *
FindCRLEntry(
IN DWORD cbSerial,
IN BYTE const *pbSerial,
IN DWORD cCRLEntry,
IN CRL_ENTRY *rgCRLEntry)
{
CRL_ENTRY *pCRLEntry = NULL;
CRL_ENTRY *rgCRLEntryEnd = &rgCRLEntry[cCRLEntry];
for ( ; rgCRLEntry < rgCRLEntryEnd; rgCRLEntry++)
{
if (cbSerial == rgCRLEntry->SerialNumber.cbData &&
0 == memcmp(pbSerial, rgCRLEntry->SerialNumber.pbData, cbSerial))
{
pCRLEntry = rgCRLEntry;
break;
}
}
return(pCRLEntry);
}
HRESULT
AddRemoveSerial(
IN WCHAR const * const *ppwszSerialList,
IN BOOL fAdd,
IN DWORD cCRLEntryIn,
IN CRL_ENTRY *rgCRLEntryIn,
OUT DWORD *pcCRLEntryOut,
OUT CRL_ENTRY **prgCRLEntryOut,
OUT DWORD *pcSerialNew,
OUT BYTE ***prgpbSerialNew)
{
HRESULT hr;
DWORD cCRLEntry = cCRLEntryIn;
CRL_ENTRY *rgCRLEntry = NULL;
DWORD cAdd;
BYTE **rgpbSerialNew = NULL;
DWORD cSerialNew;
FILETIME ftCurrent;
*prgCRLEntryOut = NULL;
*prgpbSerialNew = NULL;
cAdd = 0;
if (fAdd)
{
for (cAdd = 0; NULL != ppwszSerialList[cAdd]; cAdd++)
;
cSerialNew = 0;
rgpbSerialNew = (BYTE **) LocalAlloc(
LMEM_FIXED,
cAdd * sizeof(rgpbSerialNew[0]));
if (NULL == rgpbSerialNew)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
GetSystemTimeAsFileTime(&ftCurrent);
}
rgCRLEntry = (CRL_ENTRY *) LocalAlloc(
LMEM_FIXED,
(cCRLEntry + cAdd) * sizeof(rgCRLEntry[0]));
if (NULL == rgCRLEntry)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
CopyMemory(
rgCRLEntry,
rgCRLEntryIn,
cCRLEntry * sizeof(rgCRLEntry[0]));
if (NULL != ppwszSerialList)
{
DWORD i;
for (i = 0; NULL != ppwszSerialList[i]; i++)
{
WCHAR const *pwszSerial = ppwszSerialList[i];
CRL_ENTRY *pCRLEntry;
DWORD cbSerial;
BYTE *pbSerial;
hr = myVerifyObjId(pwszSerial);
if (S_OK == hr)
{
continue; // skip OIDs
}
hr = WszToMultiByteInteger(
FALSE,
pwszSerial,
&cbSerial,
&pbSerial);
_JumpIfErrorStr(hr, error, "WszToMultiByteInteger", pwszSerial);
pCRLEntry = FindCRLEntry(
cbSerial,
pbSerial,
cCRLEntry,
rgCRLEntry);
if (fAdd)
{
if (NULL == pCRLEntry)
{
pCRLEntry = &rgCRLEntry[cCRLEntry];
ZeroMemory(pCRLEntry, sizeof(*pCRLEntry));
pCRLEntry->SerialNumber.pbData = pbSerial;
pCRLEntry->SerialNumber.cbData = cbSerial;
pCRLEntry->RevocationDate = ftCurrent;
cCRLEntry++;
rgpbSerialNew[cSerialNew++] = pbSerial;
pbSerial = NULL;
}
}
else
{
if (NULL != pCRLEntry)
{
DWORD iDel = SAFE_SUBTRACT_POINTERS(pCRLEntry, rgCRLEntry);
if (iDel < cCRLEntry)
{
MoveMemory(
&rgCRLEntry[iDel],
&rgCRLEntry[iDel + 1],
(cCRLEntry - iDel - 1) * sizeof(rgCRLEntry[iDel]));
}
cCRLEntry--;
}
}
if (NULL != pbSerial)
{
LocalFree(pbSerial);
}
}
}
*pcCRLEntryOut = cCRLEntry;
*prgCRLEntryOut = rgCRLEntry;
rgCRLEntry = NULL;
*pcSerialNew = cSerialNew;
*prgpbSerialNew = rgpbSerialNew;
rgpbSerialNew = NULL;
hr = S_OK;
error:
if (NULL != rgCRLEntry)
{
LocalFree(rgCRLEntry);
}
if (NULL != rgpbSerialNew)
{
DWORD i;
for (i = 0; i < cSerialNew; i++)
{
if (NULL != rgpbSerialNew[i])
{
LocalFree(rgpbSerialNew[i]);
}
}
LocalFree(rgpbSerialNew);
}
return(hr);
}
HRESULT
SignCRL(
IN CRL_CONTEXT const *pCRLContext,
OPTIONAL IN FILETIME const *pftNextUpdate,
IN BOOL fAdd,
IN WCHAR const * const *ppwszSerialList,
OUT BYTE **ppbOut,
OUT DWORD *pcbOut)
{
HRESULT hr;
CRL_INFO const *pCRLInfo;
CRL_INFO CRLInfoOut;
BYTE *pbUnsigned = NULL;
DWORD cbUnsigned;
CERT_EXTENSION *pExtKeyId;
CERT_EXTENSION *rgExtension = NULL;
CRL_ENTRY *rgCRLEntry = NULL;
DWORD cSerialNew;
BYTE **rgpbSerialNew = NULL;
ZeroMemory(&CRLInfoOut, sizeof(CRLInfoOut));
*ppbOut = NULL;
// CRL extensions to strip out of the re-signed CRL:
static WCHAR const * const apwszObjIdFilter[] = {
TEXT(szOID_CRL_NEXT_PUBLISH),
NULL
};
static WCHAR const * const apwszObjIdFilterNull[] = {
NULL
};
pCRLInfo = pCRLContext->pCrlInfo;
CRLInfoOut = *pCRLInfo;
SetExpiration(
pftNextUpdate,
&CRLInfoOut.ThisUpdate,
(0 != CRLInfoOut.NextUpdate.dwLowDateTime ||
0 != CRLInfoOut.NextUpdate.dwHighDateTime)?
&CRLInfoOut.NextUpdate : NULL);
hr = cuDumpFileTime(IDS_THISUPDATE, NULL, &CRLInfoOut.ThisUpdate);
_JumpIfError(hr, error, "cuDumpFileTime");
hr = cuDumpFileTime(IDS_NEXTUPDATE, NULL, &CRLInfoOut.NextUpdate);
_JumpIfError(hr, error, "cuDumpFileTime");
wprintf(myLoadResourceString(IDS_CRLENTRIES)); // "CRL Entries:"
wprintf(L" %u\n", pCRLInfo->cCRLEntry);
wprintf(wszNewLine);
pExtKeyId = CertFindExtension(
szOID_AUTHORITY_KEY_IDENTIFIER2,
pCRLInfo->cExtension,
pCRLInfo->rgExtension);
hr = RemoveExtensions(
(NULL == pftNextUpdate ||
0 != pftNextUpdate->dwLowDateTime ||
0 != pftNextUpdate->dwHighDateTime)?
apwszObjIdFilter : apwszObjIdFilterNull,
TRUE,
CRLInfoOut.cExtension,
CRLInfoOut.rgExtension,
&CRLInfoOut.cExtension,
&rgExtension);
_JumpIfError(hr, error, "RemoveExtensions");
CRLInfoOut.rgExtension = rgExtension;
if (!fAdd)
{
CERT_EXTENSION *rgExtension2 = rgExtension;
hr = RemoveExtensions(
ppwszSerialList,
FALSE,
CRLInfoOut.cExtension,
CRLInfoOut.rgExtension,
&CRLInfoOut.cExtension,
&rgExtension);
_JumpIfError(hr, error, "RemoveExtensions");
if (NULL != rgExtension2)
{
LocalFree(rgExtension2);
}
CRLInfoOut.rgExtension = rgExtension;
}
hr = AddRemoveSerial(
ppwszSerialList,
fAdd,
CRLInfoOut.cCRLEntry,
CRLInfoOut.rgCRLEntry,
&CRLInfoOut.cCRLEntry,
&rgCRLEntry,
&cSerialNew,
&rgpbSerialNew);
_JumpIfError(hr, error, "AddRemoveSerial");
CRLInfoOut.rgCRLEntry = rgCRLEntry;
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_CERT_CRL_TO_BE_SIGNED,
&CRLInfoOut,
0,
CERTLIB_USE_LOCALALLOC,
&pbUnsigned, // pbEncoded
&cbUnsigned))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
hr = FindCertAndSign(
pExtKeyId,
NULL,
0,
pbUnsigned,
cbUnsigned,
NULL, // pwszSerialNumber
ppbOut,
pcbOut);
_JumpIfError(hr, error, "FindCertAndSign");
error:
if (NULL != rgpbSerialNew)
{
DWORD i;
for (i = 0; i < cSerialNew; i++)
{
if (NULL != rgpbSerialNew[i])
{
LocalFree(rgpbSerialNew[i]);
}
}
LocalFree(rgpbSerialNew);
}
if (NULL != rgCRLEntry)
{
LocalFree(rgCRLEntry);
}
if (NULL != rgExtension)
{
LocalFree(rgExtension);
}
if (NULL != pbUnsigned)
{
LocalFree(pbUnsigned);
}
return(hr);
}
HRESULT
SignCert(
IN CERT_CONTEXT const *pCertContext,
OPTIONAL IN WCHAR const *pwszSerialNumber,
OPTIONAL IN FILETIME const *pftNotAfter,
OPTIONAL IN WCHAR const * const *ppwszObjIdList,
OUT BYTE **ppbOut,
OUT DWORD *pcbOut)
{
HRESULT hr;
CERT_INFO const *pCertInfo;
BYTE *pbUnsigned = NULL;
DWORD cbUnsigned;
BYTE const *pbHash;
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
DWORD cbHash;
CERT_EXTENSION *pExtKeyId;
CERT_EXTENSION *rgExtension = NULL;
*ppbOut = NULL;
pExtKeyId = NULL;
if (NULL != pCertContext)
{
CERT_INFO CertInfoOut;
ZeroMemory(&CertInfoOut, sizeof(CertInfoOut));
pCertInfo = pCertContext->pCertInfo;
CertInfoOut = *pCertInfo;
SetExpiration(pftNotAfter, &CertInfoOut.NotBefore, &CertInfoOut.NotAfter);
hr = cuDumpFileTime(IDS_NOTBEFORE, NULL, &CertInfoOut.NotBefore);
_JumpIfError(hr, error, "cuDumpFileTime");
hr = cuDumpFileTime(IDS_NOTAFTER, NULL, &CertInfoOut.NotAfter);
_JumpIfError(hr, error, "cuDumpFileTime");
wprintf(wszNewLine);
pbHash = NULL;
pExtKeyId = CertFindExtension(
szOID_AUTHORITY_KEY_IDENTIFIER2,
pCertInfo->cExtension,
pCertInfo->rgExtension);
if (NULL == pExtKeyId)
{
hr = cuVerifySignature(
pCertContext->pbCertEncoded,
pCertContext->cbCertEncoded,
&pCertContext->pCertInfo->SubjectPublicKeyInfo,
FALSE,
TRUE);
if (S_OK == hr)
{
if (CertGetCertificateContextProperty(
pCertContext,
CERT_KEY_IDENTIFIER_PROP_ID,
abHash,
&cbHash))
{
pbHash = abHash;
}
}
}
hr = RemoveExtensions(
ppwszObjIdList,
TRUE,
CertInfoOut.cExtension,
CertInfoOut.rgExtension,
&CertInfoOut.cExtension,
&rgExtension);
_JumpIfError(hr, error, "RemoveExtensions");
CertInfoOut.rgExtension = rgExtension;
if (0 == CertInfoOut.cExtension)
{
CertInfoOut.dwVersion = CERT_V1;
}
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_CERT_TO_BE_SIGNED,
&CertInfoOut,
0,
CERTLIB_USE_LOCALALLOC,
&pbUnsigned, // pbEncoded
&cbUnsigned))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
}
hr = FindCertAndSign(
pExtKeyId,
pbHash,
cbHash,
pbUnsigned,
cbUnsigned,
pwszSerialNumber,
ppbOut,
pcbOut);
_JumpIfError(hr, error, "FindCertAndSign");
error:
if (NULL != rgExtension)
{
LocalFree(rgExtension);
}
if (NULL != pbUnsigned)
{
LocalFree(pbUnsigned);
}
return(hr);
}
HRESULT
verbSign(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszfnIn,
IN WCHAR const *pwszfnOut,
OPTIONAL IN WCHAR const *pwszDaysHours,
OPTIONAL IN WCHAR const *pwszChangeList)
{
HRESULT hr;
FILETIME ftNextUpdate;
FILETIME *pftNextUpdate;
CRL_CONTEXT const *pCRLContext = NULL;
CERT_CONTEXT const *pCertContext = NULL;
BYTE *pbOut = NULL;
DWORD cbOut;
BOOL fAdd = FALSE;
WCHAR **ppwszList = NULL;
BSTR strSerialNumber = NULL;
pftNextUpdate = NULL;
if (NULL != pwszDaysHours &&
(myIsMinusSign(*pwszDaysHours) || L'+' == *pwszDaysHours))
{
WCHAR const *pwsz = pwszDaysHours;
pwszDaysHours = pwszChangeList;
pwszChangeList = pwsz;
}
if (NULL != pwszChangeList)
{
if (!myIsMinusSign(*pwszChangeList) && L'+' != *pwszChangeList)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "missing +/-");
}
fAdd = L'+' == *pwszChangeList++;
hr = cuParseStrings(
pwszChangeList,
FALSE,
NULL,
NULL,
&ppwszList,
NULL);
_JumpIfError(hr, error, "cuParseStrings");
}
if (NULL != pwszDaysHours)
{
if (0 == lstrcmp(L"0", pwszDaysHours))
{
ZeroMemory(&ftNextUpdate, sizeof(ftNextUpdate));
}
else
{
hr = cuParseDaysHours(pwszDaysHours, &ftNextUpdate);
_JumpIfError(hr, error, "cuParseDaysHours");
}
pftNextUpdate = &ftNextUpdate;
}
if (NULL == pwszDaysHours &&
NULL == pwszChangeList &&
!myDoesFileExist(pwszfnIn))
{
hr = myMakeSerialBstr(pwszfnIn, &strSerialNumber);
_JumpIfError(hr, error, "myMakeSerialBstr");
hr = SignCert(
NULL, // pCertContext
strSerialNumber,
pftNextUpdate,
NULL, // ppwszObjIdList
&pbOut,
&cbOut);
_JumpIfError(hr, error, "SignCert");
}
else
{
// Load and decode CRL and certificate
hr = cuLoadCRL(pwszfnIn, &pCRLContext);
if (S_OK == hr)
{
hr = SignCRL(
pCRLContext,
pftNextUpdate,
fAdd,
ppwszList,
&pbOut,
&cbOut);
_JumpIfError(hr, error, "SignCRL");
}
else
{
hr = cuLoadCert(pwszfnIn, &pCertContext);
if (S_OK == hr)
{
if (fAdd)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "cannot add extensions to cert");
}
hr = SignCert(
pCertContext,
NULL, // pwszSerialNumber
pftNextUpdate,
ppwszList,
&pbOut,
&cbOut);
_JumpIfError(hr, error, "SignCert");
}
else
{
cuPrintError(IDS_FORMAT_LOADTESTCRL, hr);
goto error;
}
}
}
// Write encoded & signed CRL or Cert to file
hr = EncodeToFileW(
pwszfnOut,
pbOut,
cbOut,
CRYPT_STRING_BINARY | g_EncodeFlags);
if (S_OK != hr)
{
cuPrintError(IDS_ERR_FORMAT_ENCODETOFILE, hr);
goto error;
}
wprintf(
myLoadResourceString(IDS_FORMAT_OUTPUT_LENGTH), // "Output Length = %d"
cuFileSize(pwszfnOut));
wprintf(wszNewLine);
hr = S_OK;
error:
if (NULL != strSerialNumber)
{
SysFreeString(strSerialNumber);
}
cuFreeStringArray(ppwszList);
if (NULL != pbOut)
{
LocalFree(pbOut);
}
cuUnloadCRL(&pCRLContext);
cuUnloadCert(&pCertContext);
return(hr);
}
HRESULT
verbShutDownServer(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszArg1,
IN WCHAR const *pwszArg2,
IN WCHAR const *pwszArg3,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
hr = CertSrvServerControl(g_pwszConfig, CSCONTROL_SHUTDOWN, NULL, NULL);
_JumpIfError(hr, error, "CertSrvServerControl");
error:
if (E_ACCESSDENIED == hr)
{
g_uiExtraErrorInfo = IDS_ERROR_ACCESSDENIED_CAUSE;
}
return(hr);
}
HRESULT
verbIsValidCertificate(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszSerialNumber,
IN WCHAR const *pwszArg2,
IN WCHAR const *pwszArg3,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
DISPATCHINTERFACE diAdmin;
BSTR strSerialNumber = NULL;
LONG Reason = CRL_REASON_KEY_COMPROMISE;
BOOL fMustRelease = FALSE;
LONG Disposition;
hr = myMakeSerialBstr(pwszSerialNumber, &strSerialNumber);
_JumpIfError(hr, error, "myMakeSerialBstr");
hr = Admin_Init(g_DispatchFlags, &diAdmin);
_JumpIfError(hr, error, "Admin_Init");
fMustRelease = TRUE;
hr = Admin_IsValidCertificate(
&diAdmin,
g_pwszConfig,
strSerialNumber,
&Disposition);
_JumpIfError(hr, error, "Admin_IsValidCertificate");
switch (Disposition)
{
case CA_DISP_INVALID:
wprintf(myLoadResourceString(IDS_CERT_DISPOSITION_INVALID), strSerialNumber); // "Certificate disposition for "%ws" is invalid"
wprintf(wszNewLine);
break;
case CA_DISP_VALID:
wprintf(myLoadResourceString(IDS_CERT_DISPOSITION_VALID), strSerialNumber); // "Certificate disposition for "%ws" is valid"
wprintf(wszNewLine);
break;
case CA_DISP_UNDER_SUBMISSION:
wprintf(myLoadResourceString(IDS_CERT_DISPOSITION_PENDING), strSerialNumber); // "Certificate request for "%ws" is pending"
wprintf(wszNewLine);
break;
case CA_DISP_REVOKED:
hr = Admin_GetRevocationReason(&diAdmin, &Reason);
if (S_OK != hr)
{
_PrintIfError(hr, "Admin_GetRevocationReason");
Reason = CRL_REASON_UNSPECIFIED;
}
wprintf(
myLoadResourceString(IDS_CERT_DISPOSITION_REVOKED), // "Certificate disposition for "%ws" is revoked (%ws)"
strSerialNumber,
wszCRLReason(Reason));
wprintf(wszNewLine);
break;
}
error:
if (fMustRelease)
{
Admin_Release(&diAdmin);
}
if (NULL != strSerialNumber)
{
SysFreeString(strSerialNumber);
}
return(hr);
}
#define wszREQUEST L"Request"
#define wszCERT L"Cert"
HRESULT
verbDeleteRow(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszRowIdOrDate,
OPTIONAL IN WCHAR const *pwszTable,
IN WCHAR const *pwszArg3,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
WCHAR *pwszLocalTime = NULL;
DISPATCHINTERFACE diAdmin;
BOOL fMustRelease = FALSE;
DWORD Flags;
LONG RowId;
DATE date;
LONG Table;
LONG Count;
hr = myGetLong(pwszRowIdOrDate, &RowId);
if (S_OK != hr)
{
FILETIME ftCurrent;
FILETIME ftQuery;
RowId = 0;
hr = myWszLocalTimeToGMTDate(pwszRowIdOrDate, &date);
_JumpIfError(hr, error, "invalid RowId or date");
hr = myGMTDateToWszLocalTime(&date, g_fSeconds, &pwszLocalTime);
_JumpIfError(hr, error, "myGMTDateToWszLocalTime");
GetSystemTimeAsFileTime(&ftCurrent);
hr = myDateToFileTime(&date, &ftQuery);
_JumpIfError(hr, error, "myDateToFileTime");
if (0 > CompareFileTime(&ftCurrent, &ftQuery))
{
wprintf(
myLoadResourceString(IDS_FORMAT_DATE_IN_FUTURE), // "The date specified is in the future: %ws"
pwszLocalTime);
wprintf(wszNewLine);
if (!g_fForce)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "date in future");
}
}
if (g_fVerbose)
{
wprintf(pwszLocalTime);
wprintf(wszNewLine);
}
}
else
{
if (0 == RowId)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "zero RowId");
}
date = 0.0;
}
hr = E_INVALIDARG;
Table = CVRC_TABLE_REQCERT;
Flags = 0;
if (NULL == pwszTable)
{
if (0 == RowId)
{
wprintf(
myLoadResourceString(IDS_FORMAT_DATE_REQUIRES_TABLE), // "One of the following tables must be specified when deleting rows older than %ws:"
pwszLocalTime);
wprintf(wszNewLine);
wprintf(L" %ws\n", wszREQUEST);
wprintf(L" %ws\n", wszCERT);
wprintf(L" %ws\n", g_wszCRL);
_JumpError(hr, error, "date requires table");
}
}
else
if (0 == LSTRCMPIS(pwszTable, wszREQUEST))
{
Flags = CDR_REQUEST_LAST_CHANGED; // assume date query
}
else
if (0 == LSTRCMPIS(pwszTable, wszCERT))
{
Flags = CDR_EXPIRED; // assume date query
}
else
if (0 == mylstrcmpiS(pwszTable, g_wszExt))
{
Table = CVRC_TABLE_EXTENSIONS;
if (0 == RowId)
{
_JumpError(hr, error, "no date in Extension table");
}
}
else
if (0 == mylstrcmpiS(pwszTable, g_wszAttrib))
{
Table = CVRC_TABLE_ATTRIBUTES;
if (0 == RowId)
{
_JumpError(hr, error, "no date in Request Attribute table");
}
}
else
if (0 == mylstrcmpiS(pwszTable, g_wszCRL))
{
Table = CVRC_TABLE_CRL; // assume date query
}
else
{
_JumpError(hr, error, "bad table name");
}
if (0 != RowId)
{
Flags = 0; // not a date query
}
else if (g_fVerbose)
{
wprintf(L"%ws\n", pwszLocalTime);
}
hr = Admin_Init(g_DispatchFlags, &diAdmin);
_JumpIfError(hr, error, "Admin_Init");
fMustRelease = TRUE;
Count = 0;
hr = Admin2_DeleteRow(
&diAdmin,
g_pwszConfig,
Flags,
date,
Table,
RowId,
&Count);
wprintf(myLoadResourceString(IDS_FORMAT_DELETED_ROW_COUNT), Count);
wprintf(wszNewLine);
_JumpIfError(hr, error, "Admin2_DeleteRow");
error:
if (fMustRelease)
{
Admin_Release(&diAdmin);
}
if (NULL != pwszLocalTime)
{
LocalFree(pwszLocalTime);
}
return(hr);
}
HRESULT
verbSetAttributes(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszRequestId,
IN WCHAR const *pwszAttributes,
IN WCHAR const *pwszArg3,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
DISPATCHINTERFACE diAdmin;
LONG RequestId;
BSTR strAttributes = NULL;
WCHAR *pwsz;
BOOL fMustRelease = FALSE;
if (!ConvertWszToBstr(&strAttributes, pwszAttributes, MAXDWORD))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "ConvertWszToBstr");
}
for (pwsz = strAttributes; L'\0' != *pwsz; pwsz++)
{
switch (*pwsz)
{
case L';':
*pwsz = L'\n';
break;
case L'\\':
if (L'n' == pwsz[1])
{
*pwsz++ = L'\r';
*pwsz = L'\n';
}
break;
}
}
hr = myGetLong(pwszRequestId, &RequestId);
_JumpIfError(hr, error, "RequestId must be a number");
hr = Admin_Init(g_DispatchFlags, &diAdmin);
_JumpIfError(hr, error, "Admin_Init");
fMustRelease = TRUE;
hr = Admin_SetRequestAttributes(
&diAdmin,
g_pwszConfig,
RequestId,
strAttributes);
_JumpIfError(hr, error, "Admin_SetAttributes");
error:
if (fMustRelease)
{
Admin_Release(&diAdmin);
}
if (NULL != strAttributes)
{
SysFreeString(strAttributes);
}
return(hr);
}
HRESULT
verbSetExtension(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszRequestId,
IN WCHAR const *pwszExtensionName,
IN WCHAR const *pwszFlags,
IN WCHAR const *pwszValue)
{
HRESULT hr;
DISPATCHINTERFACE diAdmin;
LONG RequestId;
LONG Flags;
BSTR strExtensionName = NULL;
BSTR strValue = NULL;
LONG PropType;
VARIANT var;
BOOL fMustRelease = FALSE;
BYTE *pbValue = NULL;
DWORD cbValue;
hr = myGetLong(pwszRequestId, &RequestId);
_JumpIfError(hr, error, "RequestId must be a number");
if (!ConvertWszToBstr(&strExtensionName, pwszExtensionName, MAXDWORD))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "ConvertWszToBstr");
}
hr = myGetLong(pwszFlags, &Flags);
_JumpIfError(hr, error, "Flags must be a number");
if (~EXTENSION_POLICY_MASK & Flags)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "Flags must be <= 0xffff");
}
if (L'@' == *pwszValue)
{
pwszValue++;
// Read in and decode the extension from a file.
// Try Hex-Ascii, Base64 with and without a header, then binary.
hr = DecodeFileW(pwszValue, &pbValue, &cbValue, CRYPT_STRING_HEX_ANY);
if (S_OK != hr)
{
hr = DecodeFileW(pwszValue, &pbValue, &cbValue, CRYPT_STRING_ANY);
_JumpIfError(hr, error, "DecodeFileW");
}
CSASSERT(NULL != pbValue && 0 != cbValue);
var.vt = VT_BSTR;
PropType = PROPTYPE_BINARY;
DumpHex(0, pbValue, cbValue);
if (!ConvertWszToBstr(&strValue, (WCHAR const *) pbValue, cbValue))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "ConvertWszToBstr");
}
var.bstrVal = strValue;
}
else
{
hr = myGetLong(pwszValue, &var.lVal);
if (S_OK == hr)
{
var.vt = VT_I4;
PropType = PROPTYPE_LONG;
}
else
{
hr = myWszLocalTimeToGMTDate(pwszValue, &var.date);
if (S_OK == hr)
{
var.vt = VT_DATE;
PropType = PROPTYPE_DATE;
}
else
{
PropType = PROPTYPE_STRING;
if (!ConvertWszToBstr(&strValue, pwszValue, MAXDWORD))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "ConvertWszToBstr");
}
var.vt = VT_BSTR;
var.bstrVal = strValue;
}
}
}
hr = Admin_Init(g_DispatchFlags, &diAdmin);
if (S_OK != hr)
{
_JumpError(hr, error, "Admin_Init");
}
fMustRelease = TRUE;
hr = Admin_SetCertificateExtension(
&diAdmin,
g_pwszConfig,
RequestId,
strExtensionName,
PropType,
Flags,
&var);
_JumpIfError(hr, error, "Admin_SetExtension");
error:
if (NULL != pbValue)
{
LocalFree(pbValue);
}
if (fMustRelease)
{
Admin_Release(&diAdmin);
}
if (NULL != strExtensionName)
{
SysFreeString(strExtensionName);
}
if (NULL != strValue)
{
SysFreeString(strValue);
}
return(hr);
}
HRESULT
verbImportCertificate(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszCertificateFile,
IN WCHAR const *pwszArg2,
IN WCHAR const *pwszArg3,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
LONG dwReqID;
CERT_CONTEXT const *pCertContext = NULL;
DISPATCHINTERFACE diAdmin;
BOOL fRelease = FALSE;
hr = Admin_Init(g_DispatchFlags, &diAdmin);
_JumpIfError(hr, error, "Admin_Init");
fRelease = TRUE;
hr = cuLoadCert(pwszCertificateFile, &pCertContext);
_JumpIfError(hr, error, "cuLoadCert");
hr = Admin_ImportCertificate(
&diAdmin,
g_pwszConfig,
(WCHAR const *) pCertContext->pbCertEncoded,
pCertContext->cbCertEncoded,
(g_fForce? ICF_ALLOWFOREIGN : 0) | CR_IN_BINARY,
&dwReqID);
_JumpIfError(hr, error, "Admin_ImportCertificate");
wprintf(myLoadResourceString(IDS_FORMAT_IMPORTCERT), dwReqID);
wprintf(wszNewLine);
error:
cuUnloadCert(&pCertContext);
if (fRelease)
{
Admin_Release(&diAdmin);
}
return(hr);
}
HRESULT
DumpKeyRecipientInfo(
IN BYTE const *pbRecoveryBlob,
IN DWORD cbRecoveryBlob)
{
HRESULT hr;
BYTE *pbEncryptedKey = NULL;
DWORD cbEncryptedKey;
DWORD cRecipient;
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
DWORD dwMsgType;
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
hr = myDecodePKCS7(
pbRecoveryBlob,
cbRecoveryBlob,
&pbEncryptedKey,
&cbEncryptedKey,
&dwMsgType,
NULL, // ppszInnerContentObjId
NULL, // pcSigner
NULL, // pcRecipient
&hStore,
NULL); // phMsg
_JumpIfError(hr, error, "myDecodePKCS7");
if (NULL == pbEncryptedKey)
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "No Content");
}
if (CMSG_SIGNED != dwMsgType)
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "Not Signed");
}
hr = myDecodePKCS7(
pbEncryptedKey,
cbEncryptedKey,
NULL, // ppbContent
NULL, // pcbContent
&dwMsgType,
NULL, // ppszInnerContentObjId
NULL, // pcSigner
&cRecipient,
NULL, // phStore
&hMsg);
_JumpIfError(hr, error, "myDecodePKCS7");
if (CMSG_ENVELOPED != dwMsgType)
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "Not Encrypted");
}
if (NULL == hMsg || 0 == cRecipient)
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "No Msg or Recipients");
}
hr = cuDumpRecipients(hMsg, hStore, cRecipient, TRUE);
_JumpIfError(hr, error, "cuDumpRecipients");
error:
if (NULL != hMsg)
{
CryptMsgClose(hMsg);
}
if (NULL != hStore)
{
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
}
if (NULL != pbEncryptedKey)
{
LocalFree(pbEncryptedKey);
}
return(hr);
}
HRESULT
GetArchivedKey(
IN WCHAR const *pwszConfig,
IN DWORD RequestId,
OPTIONAL IN WCHAR const *pwszfnRecoveryBlob)
{
HRESULT hr;
DISPATCHINTERFACE diAdmin;
BOOL fRelease = FALSE;
BSTR strKey = NULL;
WCHAR *pwszT = NULL;
hr = Admin_Init(g_DispatchFlags, &diAdmin);
_JumpIfError(hr, error, "Admin_Init");
fRelease = TRUE;
hr = Admin2_GetArchivedKey(
&diAdmin,
pwszConfig,
RequestId,
CR_OUT_BINARY,
&strKey);
_JumpIfError(hr, error, "Admin_GetArchivedKey");
if (NULL == pwszfnRecoveryBlob)
{
hr = myCryptBinaryToString(
(BYTE const *) strKey,
SysStringByteLen(strKey),
CRYPT_STRING_BASE64HEADER,
&pwszT);
_JumpIfError(hr, error, "myCryptBinaryToString");
cuPrintCRLFString(NULL, pwszT);
}
else
{
hr = EncodeToFileW(
pwszfnRecoveryBlob,
(BYTE const *) strKey,
SysStringByteLen(strKey),
CRYPT_STRING_BINARY | g_EncodeFlags);
_JumpIfError(hr, error, "EncodeToFileW");
}
hr = DumpKeyRecipientInfo((BYTE const *) strKey, SysStringByteLen(strKey));
_PrintIfError(hr, "DumpKeyRecipientInfo");
hr = S_OK;
error:
if (NULL != pwszT)
{
LocalFree(pwszT);
}
if (NULL != strKey)
{
SysFreeString(strKey);
}
if (fRelease)
{
Admin_Release(&diAdmin);
}
return(hr);
}
typedef struct _GETKEYSERIAL {
struct _GETKEYSERIAL *Next;
DWORD dwVersion;
BSTR strConfig;
LONG RequestId;
BSTR strSerialNumber;
BSTR strCommonName;
BSTR strUPN;
BSTR strHash;
BSTR strCert;
} GETKEYSERIAL;
VOID
FreeKeySerialEntry(
IN OUT GETKEYSERIAL *pks)
{
if (NULL != pks->strConfig)
{
SysFreeString(pks->strConfig);
}
if (NULL != pks->strSerialNumber)
{
SysFreeString(pks->strSerialNumber);
}
if (NULL != pks->strCommonName)
{
SysFreeString(pks->strCommonName);
}
if (NULL != pks->strUPN)
{
SysFreeString(pks->strUPN);
}
if (NULL != pks->strHash)
{
SysFreeString(pks->strHash);
}
if (NULL != pks->strCert)
{
SysFreeString(pks->strCert);
}
LocalFree(pks);
}
HRESULT
AddKeySerialList(
IN WCHAR const *pwszConfig,
IN LONG RequestId,
IN WCHAR const *pwszSerialNumber,
IN WCHAR const *pwszCommonName,
IN WCHAR const *pwszUPN,
IN WCHAR const *pwszHash,
IN BYTE const *pbCert,
IN DWORD cbCert,
IN OUT GETKEYSERIAL **ppksList)
{
HRESULT hr;
CERT_CONTEXT const *pcc = NULL;
GETKEYSERIAL *pksNew = NULL;
GETKEYSERIAL *pksT;
GETKEYSERIAL *pksPrev;
BOOL fNewConfig = TRUE;
pcc = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert);
if (NULL == pcc)
{
hr = myHLastError();
_JumpError(hr, error, "CertCreateCertificateContext");
}
pksNew = (GETKEYSERIAL *) LocalAlloc(
LMEM_FIXED | LMEM_ZEROINIT,
sizeof(*pksNew));
if (NULL == pksNew)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
pksNew->RequestId = RequestId;
pksNew->dwVersion = pcc->pCertInfo->dwVersion;
pksNew->strConfig = SysAllocString(pwszConfig);
pksNew->strSerialNumber = SysAllocString(pwszSerialNumber);
pksNew->strCommonName = SysAllocString(pwszCommonName);
pksNew->strUPN = SysAllocString(pwszUPN);
pksNew->strHash = SysAllocString(pwszHash);
pksNew->strCert = SysAllocStringByteLen((char const *) pbCert, cbCert);
if (NULL == pksNew->strConfig ||
NULL == pksNew->strSerialNumber ||
(NULL != pwszCommonName && NULL == pksNew->strCommonName) ||
(NULL != pwszUPN && NULL == pksNew->strUPN) ||
NULL == pksNew->strHash ||
NULL == pksNew->strCert)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
pksPrev = NULL;
for (pksT = *ppksList; NULL != pksT; pksT = pksT->Next)
{
if (NULL != pksT->strConfig)
{
fNewConfig = 0 != lstrcmp(pksT->strConfig, pksNew->strConfig);
}
pksPrev = pksT;
}
if (NULL == pksPrev)
{
*ppksList = pksNew;
}
else
{
pksPrev->Next = pksNew;
}
if (!fNewConfig)
{
SysFreeString(pksNew->strConfig);
pksNew->strConfig = NULL;
}
pksNew = NULL;
hr = S_OK;
error:
if (NULL != pcc)
{
CertFreeCertificateContext(pcc);
}
if (NULL != pksNew)
{
FreeKeySerialEntry(pksNew);
}
return(hr);
}
HRESULT
cuViewQueryWorker(
IN WCHAR const *pwszConfig,
IN WCHAR const *pwszColumn,
IN WCHAR const *pwszValue1,
OPTIONAL IN WCHAR const *pwszValue2,
IN OUT GETKEYSERIAL **ppksList,
OUT BOOL *pfConnectionFailed)
{
HRESULT hr;
DWORD cwc;
DISPATCHINTERFACE diView;
DISPATCHINTERFACE diViewRow;
DISPATCHINTERFACE diViewColumn;
BOOL fMustRelease = FALSE;
BOOL fMustReleaseRow = FALSE;
BOOL fMustReleaseColumn = FALSE;
LONG ColIndex;
LONG RowIndex;
DWORD cRow;
VARIANT var;
LONG RequestId;
DWORD i;
static WCHAR *apwszCol[] =
{
#define IV_REQUESTID 0
wszPROPCERTIFICATEREQUESTID,
#define IV_SERIALNUMBER 1
wszPROPCERTIFICATESERIALNUMBER,
#define IV_COMMONNAME 2
wszPROPCOMMONNAME,
#define IV_ARCHIVEDKEY 3
wszPROPREQUESTRAWARCHIVEDKEY,
#define IV_HASH 4
wszPROPCERTIFICATEHASH,
#define IV_CERT 5
wszPROPRAWCERTIFICATE,
#define IV_UPN 6
wszPROPCERTIFICATEUPN,
};
static LONG altype[] =
{
PROPTYPE_LONG,
PROPTYPE_STRING,
PROPTYPE_STRING,
PROPTYPE_BINARY,
PROPTYPE_STRING,
PROPTYPE_BINARY,
PROPTYPE_STRING,
};
BSTR astrValue[ARRAYSIZE(apwszCol)];
ZeroMemory(astrValue, sizeof(astrValue));
VariantInit(&var);
*pfConnectionFailed = TRUE;
DBGPRINT((
DBG_SS_CERTUTILI,
"Query(%ws, %ws == %ws + %ws)\n",
pwszConfig,
pwszColumn,
pwszValue1,
pwszValue2));
hr = View_Init(g_DispatchFlags, &diView);
_JumpIfError(hr, error, "View_Init");
fMustRelease = TRUE;
hr = View_OpenConnection(&diView, pwszConfig);
_JumpIfError(hr, error, "View_OpenConnection");
*pfConnectionFailed = FALSE;
hr = View_GetColumnIndex(
&diView,
CVRC_COLUMN_SCHEMA,
pwszColumn,
&ColIndex);
_JumpIfErrorStr(hr, error, "View_GetColumnIndex", pwszColumn);
cwc = wcslen(pwszValue1);
if (NULL != pwszValue2)
{
cwc += wcslen(pwszValue2);
}
var.bstrVal = SysAllocStringLen(NULL, cwc);
if (NULL == var.bstrVal)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "SysAllocString");
}
var.vt = VT_BSTR;
wcscpy(var.bstrVal, pwszValue1);
if (NULL != pwszValue2)
{
wcscat(var.bstrVal, pwszValue2);
}
hr = View_SetRestriction(
&diView,
ColIndex, // Restriction ColumnIndex
CVR_SEEK_EQ,
CVR_SORT_ASCEND,
&var); // pvarValue
_JumpIfError(hr, error, "View_SetRestriction");
hr = View_SetResultColumnCount(&diView, ARRAYSIZE(apwszCol));
_JumpIfError(hr, error, "View_SetResultColumnCount");
for (i = 0; i < ARRAYSIZE(apwszCol); i++)
{
hr = View_GetColumnIndex(
&diView,
CVRC_COLUMN_SCHEMA,
apwszCol[i],
&ColIndex);
_JumpIfErrorStr(
hr,
error,
"View_GetColumnIndex",
apwszCol[i]);
hr = View_SetResultColumn(&diView, ColIndex);
_JumpIfError(hr, error, "View_SetResultColumn");
}
hr = View_OpenView(&diView, &diViewRow);
_JumpIfError(hr, error, "View_OpenView");
fMustReleaseRow = TRUE;
for (cRow = 0; ; cRow++)
{
hr = ViewRow_Next(&diViewRow, &RowIndex);
if (S_FALSE == hr || (S_OK == hr && -1 == RowIndex))
{
break;
}
_JumpIfError(hr, error, "ViewRow_Next");
if (fMustReleaseColumn)
{
ViewColumn_Release(&diViewColumn);
fMustReleaseColumn = FALSE;
}
hr = ViewRow_EnumCertViewColumn(&diViewRow, &diViewColumn);
_JumpIfError(hr, error, "ViewRow_EnumCertViewColumn");
fMustReleaseColumn = TRUE;
for (i = 0; i < ARRAYSIZE(apwszCol); i++)
{
VOID *pv;
hr = ViewColumn_Next(&diViewColumn, &ColIndex);
if (S_FALSE == hr || (S_OK == hr && -1 == ColIndex))
{
break;
}
_JumpIfError(hr, error, "ViewColumn_Next");
pv = &RequestId;
if (PROPTYPE_LONG != altype[i])
{
pv = &astrValue[i];
}
hr = ViewColumn_GetValue(
&diViewColumn,
CV_OUT_BINARY,
altype[i],
pv);
if (S_OK != hr)
{
_PrintErrorStr2(
hr,
"ViewColumn_GetValue",
apwszCol[i],
CERTSRV_E_PROPERTY_EMPTY);
if (CERTSRV_E_PROPERTY_EMPTY != hr)
{
goto error;
}
}
}
DBGPRINT((
DBG_SS_CERTUTILI,
"RequestId=%u Serial=%ws CN=%ws UPN=%ws Key=%u\n",
RequestId,
astrValue[IV_SERIALNUMBER],
astrValue[IV_COMMONNAME],
astrValue[IV_UPN],
NULL != astrValue[IV_ARCHIVEDKEY]));
if (NULL != astrValue[IV_SERIALNUMBER] &&
NULL != astrValue[IV_HASH] &&
NULL != astrValue[IV_CERT] &&
(g_fForce || NULL != astrValue[IV_ARCHIVEDKEY]))
{
hr = AddKeySerialList(
pwszConfig,
RequestId,
astrValue[IV_SERIALNUMBER],
astrValue[IV_COMMONNAME],
astrValue[IV_UPN],
astrValue[IV_HASH],
(BYTE const *) astrValue[IV_CERT],
SysStringByteLen(astrValue[IV_CERT]),
ppksList);
_JumpIfError(hr, error, "AddKeySerialList");
}
for (i = 0; i < ARRAYSIZE(astrValue); i++)
{
if (NULL != astrValue[i])
{
SysFreeString(astrValue[i]);
astrValue[i] = NULL;
}
}
}
hr = S_OK;
error:
if (fMustReleaseColumn)
{
ViewColumn_Release(&diViewColumn);
}
if (fMustReleaseRow)
{
ViewRow_Release(&diViewRow);
}
if (fMustRelease)
{
View_Release(&diView);
}
for (i = 0; i < ARRAYSIZE(astrValue); i++)
{
if (NULL != astrValue[i])
{
SysFreeString(astrValue[i]);
}
}
VariantClear(&var);
return(hr);
}
// Print the string, except for the newline at the start or end of the string.
VOID
PutStringStripNL(
IN WCHAR const *pwszValue)
{
DWORD cwc;
cwc = wcslen(pwszValue);
if (L'\n' == *pwszValue)
{
pwszValue++;
cwc--;
}
else if (NULL != wcschr(pwszValue, L'\n'))
{
cwc--;
CSASSERT('\n' == pwszValue[cwc]);
}
wprintf(L"%.*ws", cwc, pwszValue);
}
HRESULT
cuViewQuery(
IN WCHAR const *pwszConfig,
IN WCHAR const *pwszColumn,
IN WCHAR const *pwszValue1,
OPTIONAL IN WCHAR const *pwszValue2,
IN OUT GETKEYSERIAL **ppksList,
OUT BOOL *pfConnectionFailed)
{
HRESULT hr;
if (!g_fQuiet)
{
if (g_fVerbose)
{
wprintf(L" %ws: ", pwszColumn);
PutStringStripNL(pwszValue1);
if (NULL != pwszValue2)
{
wprintf(L" + ");
PutStringStripNL(pwszValue2);
}
wprintf(wszNewLine);
}
else
{
wprintf(L"...");
}
}
hr = cuViewQueryWorker(
pwszConfig,
pwszColumn,
pwszValue1,
pwszValue2,
ppksList,
pfConnectionFailed);
_JumpIfError(hr, error, "cuViewQueryWorker");
error:
return(hr);
}
#define wszCOMPUTERS L"Computers"
#define wszUSERS L"Users"
#define wszRECIPIENTS L"recipients"
#define wszCOMPUTERSNL wszCOMPUTERS L"\n"
#define wszUSERSNL wszUSERS L"\n"
#define wszRECIPIENTSNL wszRECIPIENTS L"\n"
#define wszNLRECIPIENTS L"\n" wszRECIPIENTS
HRESULT
GetKey(
IN WCHAR const *pwszConfig,
IN WCHAR const *pwszCommonName,
OPTIONAL IN WCHAR const *pwszRequesterName,
OPTIONAL IN WCHAR const *pwszUPN,
OPTIONAL IN WCHAR const *pwszSerialNumber,
OPTIONAL IN WCHAR const *pwszHash,
IN OUT GETKEYSERIAL **ppksList)
{
HRESULT hr;
BOOL fConnectionFailed;
if (!g_fQuiet)
{
wprintf(myLoadResourceString(IDS_FORMAT_QUERYING), pwszConfig);
if (g_fVerbose)
{
wprintf(wszNewLine);
}
}
hr = cuViewQuery(
pwszConfig,
wszPROPCOMMONNAME,
pwszCommonName,
NULL,
ppksList,
&fConnectionFailed);
_PrintIfErrorStr(hr, "cuViewQuery", wszPROPSUBJECTCOMMONNAME);
if (S_OK != hr)
{
if (fConnectionFailed)
{
goto error;
}
}
if (NULL == wcschr(pwszCommonName, L'\n'))
{
hr = cuViewQuery(
pwszConfig,
wszPROPCOMMONNAME,
wszCOMPUTERSNL,
pwszCommonName,
ppksList,
&fConnectionFailed);
_PrintIfErrorStr(hr, "cuViewQuery", wszCOMPUTERS L"+" wszPROPSUBJECTCOMMONNAME );
hr = cuViewQuery(
pwszConfig,
wszPROPCOMMONNAME,
wszUSERSNL,
pwszCommonName,
ppksList,
&fConnectionFailed);
_PrintIfErrorStr(hr, "cuViewQuery", wszUSERS L"+" wszPROPSUBJECTCOMMONNAME );
hr = cuViewQuery(
pwszConfig,
wszPROPCOMMONNAME,
wszRECIPIENTSNL,
pwszCommonName,
ppksList,
&fConnectionFailed);
_PrintIfErrorStr(hr, "cuViewQuery", wszRECIPIENTS L"+" wszPROPSUBJECTCOMMONNAME);
hr = cuViewQuery(
pwszConfig,
wszPROPCOMMONNAME,
pwszCommonName,
wszNLRECIPIENTS,
ppksList,
&fConnectionFailed);
_PrintIfErrorStr(hr, "cuViewQuery", wszPROPSUBJECTCOMMONNAME L"+" wszRECIPIENTS);
}
if (NULL != pwszSerialNumber)
{
hr = cuViewQuery(
pwszConfig,
wszPROPCERTIFICATESERIALNUMBER,
pwszSerialNumber,
NULL,
ppksList,
&fConnectionFailed);
_PrintIfErrorStr(hr, "cuViewQuery", wszPROPCERTIFICATESERIALNUMBER);
}
if (NULL != pwszHash)
{
hr = cuViewQuery(
pwszConfig,
wszPROPCERTIFICATEHASH,
pwszHash,
NULL,
ppksList,
&fConnectionFailed);
_PrintIfErrorStr(hr, "cuViewQuery", wszPROPCERTIFICATEHASH);
}
if (NULL != pwszRequesterName)
{
hr = cuViewQuery(
pwszConfig,
wszPROPREQUESTERNAME,
pwszRequesterName,
NULL,
ppksList,
&fConnectionFailed);
_PrintIfErrorStr(hr, "cuViewQuery", wszPROPREQUESTERNAME);
}
if (NULL != pwszUPN)
{
hr = cuViewQuery(
pwszConfig,
wszPROPCERTIFICATEUPN,
pwszUPN,
NULL,
ppksList,
&fConnectionFailed);
_PrintIfErrorStr(hr, "cuViewQuery", wszPROPCERTIFICATEUPN);
}
if (!g_fQuiet)
{
wprintf(wszNewLine);
}
hr = S_OK;
error:
return(hr);
}
VOID
cuConvertEscapeSequences(
IN OUT WCHAR *pwsz)
{
WCHAR *pwszSrc = pwsz;
WCHAR *pwszDst = pwsz;
while (L'\0' != *pwszSrc)
{
WCHAR wc = *pwszSrc++;
if (L'\\' == wc)
{
switch (*pwszSrc)
{
case 'n':
wc = L'\n';
pwszSrc++;
break;
case 'r':
wc = L'\r';
pwszSrc++;
break;
case 't':
wc = L'\t';
pwszSrc++;
break;
default:
break;
}
}
*pwszDst++ = wc;
}
*pwszDst = L'\0';
}
WCHAR *
SplitToken(
IN OUT WCHAR **ppwszIn,
IN WCHAR *pwcSeparator)
{
WCHAR *pwszOut = NULL;
WCHAR *pwszNext = NULL;
WCHAR *pwszIn;
WCHAR *pwsz;
pwszIn = *ppwszIn;
if (NULL != pwszIn)
{
pwszOut = pwszIn;
pwsz = wcschr(pwszIn, *pwcSeparator);
if (NULL != pwsz)
{
*pwsz++ = L'\0';
pwszNext = pwsz;
}
}
*ppwszIn = pwszNext;
return(pwszOut);
}
HRESULT
SimplifyCommonName(
OPTIONAL IN WCHAR const *pwszCommonName,
OUT WCHAR **ppwszSimpleName)
{
HRESULT hr;
WCHAR *pwszDup = NULL;
WCHAR *pwszRemain;
WCHAR const *pwszToken;
*ppwszSimpleName = NULL;
if (NULL == pwszCommonName)
{
pwszCommonName = L"EmptyCN";
}
hr = myDupString(pwszCommonName, &pwszDup);
_JumpIfError(hr, error, "myDupString");
pwszRemain = pwszDup;
while (TRUE)
{
pwszToken = SplitToken(&pwszRemain, wszNAMESEPARATORDEFAULT);
if (NULL == pwszToken)
{
pwszToken = pwszCommonName;
break;
}
if (0 != LSTRCMPIS(pwszToken, wszUSERS) &&
0 != LSTRCMPIS(pwszToken, wszRECIPIENTS))
{
break;
}
}
hr = mySanitizeName(pwszToken, ppwszSimpleName);
_JumpIfError(hr, error, "mySanitizeName");
error:
if (NULL != pwszDup)
{
LocalFree(pwszDup);
}
return(hr);
}
WCHAR const *
wszBatchPassword(
IN DWORD Index,
OPTIONAL IN WCHAR const *pwszPassword)
{
static WCHAR wsz0[2 * cwcAUTOPASSWORDMAX + 1];
static WCHAR wsz1[2 * cwcAUTOPASSWORDMAX + 1];
WCHAR const *pwszRet;
WCHAR *pwsz = 0 == Index? wsz0 : wsz1;
CSASSERT(0 == Index || 1 == Index);
CSASSERT(ARRAYSIZE(wsz0) == ARRAYSIZE(wsz1));
if (NULL == pwszPassword)
{
SecureZeroMemory(pwsz, sizeof(wsz0)); // password data
pwszRet = NULL;
}
else
{
CSASSERT(ARRAYSIZE(wsz0) / 2 >= wcslen(pwszPassword));
if (NULL == wcschr(pwszPassword, L'%'))
{
pwszRet = pwszPassword;
}
else
{
WCHAR const *pwszIn;
WCHAR *pwszEnd;
pwszIn = pwszPassword;
pwszEnd = &pwsz[ARRAYSIZE(wsz0)];
pwszRet = pwsz;
while (pwsz < pwszEnd && L'\0' != (*pwsz = *pwszIn++))
{
if (L'%' == *pwsz++)
{
*pwsz++ = L'%';
}
}
if (L'\0' != *pwsz)
{
pwszRet = pwszPassword;
}
}
}
return(pwszRet);
}
HRESULT
DumpGetRecoverMergeCommandLine(
OPTIONAL IN BSTR const strConfig, // NULL -> -RecoverKey command line
IN BOOL fRecoverKey,
IN GETKEYSERIAL const *pks,
OPTIONAL IN WCHAR const *pwszPassword,
OPTIONAL IN WCHAR const *pwszSuffix,
OPTIONAL OUT WCHAR **ppwszSimpleName)
{
HRESULT hr;
CERT_CONTEXT const *pcc = NULL;
CERT_INFO *pCertInfo;
BSTR strSerialNumber = NULL;
WCHAR *pwszSimpleName = NULL;
if (NULL != ppwszSimpleName)
{
*ppwszSimpleName = NULL;
}
pcc = CertCreateCertificateContext(
X509_ASN_ENCODING,
(BYTE const *) pks->strCert,
SysStringByteLen(pks->strCert));
if (NULL == pcc)
{
hr = myHLastError();
_JumpError(hr, error, "CertCreateCertificateContext");
}
pCertInfo = pcc->pCertInfo;
// Convert serial number to string
hr = MultiByteIntegerToBstr(
FALSE,
pCertInfo->SerialNumber.cbData,
pCertInfo->SerialNumber.pbData,
&strSerialNumber);
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
hr = SimplifyCommonName(pks->strCommonName, &pwszSimpleName);
_JumpIfError(hr, error, "SimplifyCommonName");
if (NULL != strConfig)
{
wprintf(
L"%ws -config \"%ws\" -getkey %ws \"%ws-%ws%ws\"\n\n",
g_pwszProg,
strConfig,
strSerialNumber,
pwszSimpleName,
strSerialNumber,
wszRECSUFFIX);
}
else if (fRecoverKey)
{
wprintf(
L"%ws -p \"%ws\" -recoverkey -user \"%ws-%ws%ws\" \"%ws-%ws%ws\"\n\n",
g_pwszProg,
wszBatchPassword(0, pwszPassword),
pwszSimpleName,
strSerialNumber,
wszRECSUFFIX,
pwszSimpleName,
strSerialNumber,
wszP12SUFFIX);
wszBatchPassword(0, NULL); // password data
}
else // else just print the filename (for -MergePFX or delete)
{
wprintf(
L"\"%ws-%ws%ws\"",
pwszSimpleName,
strSerialNumber,
pwszSuffix);
}
if (NULL != ppwszSimpleName)
{
*ppwszSimpleName = pwszSimpleName;
pwszSimpleName = NULL;
}
hr = S_OK;
error:
if (NULL != pcc)
{
CertFreeCertificateContext(pcc);
}
if (NULL != strSerialNumber)
{
SysFreeString(strSerialNumber);
}
if (NULL != pwszSimpleName)
{
LocalFree(pwszSimpleName);
}
return(hr);
}
HRESULT
DumpRecoveryCandidate(
IN GETKEYSERIAL const *pks)
{
HRESULT hr;
CERT_CONTEXT const *pcc = NULL;
CERT_INFO *pCertInfo;
pcc = CertCreateCertificateContext(
X509_ASN_ENCODING,
(BYTE const *) pks->strCert,
SysStringByteLen(pks->strCert));
if (NULL == pcc)
{
hr = myHLastError();
_JumpError(hr, error, "CertCreateCertificateContext");
}
pCertInfo = pcc->pCertInfo;
hr = cuDumpSerial(g_wszPad2, IDS_SERIAL, &pCertInfo->SerialNumber);
_JumpIfError(hr, error, "cuDumpSerial");
hr = cuDisplayCertName(
FALSE, // fMultiLine
g_wszPad2,
myLoadResourceString(IDS_SUBJECT), // "Subject"
g_wszPad2,
&pCertInfo->Subject,
pCertInfo);
_JumpIfError(hr, error, "cuDisplayCertName(Subject)");
if (NULL != pks->strUPN)
{
wprintf(g_wszPad2);
wprintf(myLoadResourceString(IDS_UPN_COLON)); // "UPN:"
wprintf(L"%ws\n", pks->strUPN);
}
wprintf(g_wszPad2);
hr = cuDumpFileTime(IDS_NOTBEFORE, NULL, &pCertInfo->NotBefore);
_JumpIfError(hr, error, "cuDumpFileTime");
wprintf(g_wszPad2);
hr = cuDumpFileTime(IDS_NOTAFTER, NULL, &pCertInfo->NotAfter);
_JumpIfError(hr, error, "cuDumpFileTime");
hr = cuDumpCertType(g_wszPad2, pCertInfo);
_PrintIfError(hr, "cuDumpCertType");
wprintf(g_wszPad2);
wprintf(
myLoadResourceString(IDS_FORMAT_VERSION), // "Version: %u"
1 + pks->dwVersion);
wprintf(wszNewLine);
wprintf(g_wszPad2);
hr = cuDisplayHash(NULL, pcc, NULL, CERT_SHA1_HASH_PROP_ID, L"sha1");
_JumpIfError(hr, error, "cuDisplayHash");
error:
wprintf(wszNewLine);
if (NULL != pcc)
{
CertFreeCertificateContext(pcc);
}
return(hr);
}
VOID
DumpRecoveryCommandLines(
IN GETKEYSERIAL const *pksList,
IN DWORD dwVersion,
IN DWORD cCandidate,
IN WCHAR const *pwszPasswordIntermediate,
IN WCHAR const *pwszPasswordFinal)
{
GETKEYSERIAL const *pksT;
BSTR strConfigT;
DWORD cPFX;
WCHAR *pwszSimpleName = NULL;
strConfigT = NULL;
wprintf(L"\n@echo ");
wprintf(
myLoadResourceString(IDS_FORMAT_RECOVER_VERSIONX_KEYS_COLON), // "Version %u certificates and keys:"
1 + dwVersion);
wprintf(wszNewLine);
// generate certutil -getkey commands:
for (pksT = pksList; NULL != pksT; pksT = pksT->Next)
{
if (NULL != pksT->strConfig)
{
strConfigT = pksT->strConfig;
}
if ((CERT_V1 == dwVersion && CERT_V1 == pksT->dwVersion) ||
(CERT_V1 != dwVersion && CERT_V1 != pksT->dwVersion))
{
DumpGetRecoverMergeCommandLine(
strConfigT,
FALSE, // fRecoverKey
pksT,
NULL, // pwszPassword
NULL, // pwszSuffix
NULL); // ppwszSimpleName
}
}
// generate certutil -recoverkey commands:
for (pksT = pksList; NULL != pksT; pksT = pksT->Next)
{
if ((CERT_V1 == dwVersion && CERT_V1 == pksT->dwVersion) ||
(CERT_V1 != dwVersion && CERT_V1 != pksT->dwVersion))
{
DumpGetRecoverMergeCommandLine(
NULL, // strConfig
TRUE, // fRecoverKey
pksT,
pwszPasswordIntermediate,
NULL, // pwszSuffix
NULL); // ppwszSimpleName
}
}
// generate certutil -MergePFX command:
cPFX = 0;
wprintf(
L"%ws -p \"%ws\",\"%ws\" -MergePFX -user ",
g_pwszProg,
wszBatchPassword(0, pwszPasswordIntermediate),
wszBatchPassword(1, CERT_V1 == dwVersion? pwszPasswordIntermediate : pwszPasswordFinal));
wszBatchPassword(0, NULL); // password data
wszBatchPassword(1, NULL); // password data
for (pksT = pksList; NULL != pksT; pksT = pksT->Next)
{
if ((CERT_V1 == dwVersion && CERT_V1 == pksT->dwVersion) ||
(CERT_V1 != dwVersion && CERT_V1 != pksT->dwVersion))
{
if (0 != cPFX)
{
wprintf(L",");
}
DumpGetRecoverMergeCommandLine(
NULL, // strConfig
FALSE, // fRecoverKey
pksT,
NULL, // pwszPassword
wszP12SUFFIX,
0 == cPFX? &pwszSimpleName : NULL);
cPFX++;
}
}
CSASSERT(cCandidate == cPFX);
wprintf(
L" \"%ws%ws%ws\"\n\n",
pwszSimpleName,
CERT_V1 == dwVersion? wszV1SUFFIX : L"",
wszP12SUFFIX);
// generate intermediate file delete commands:
for (pksT = pksList; NULL != pksT; pksT = pksT->Next)
{
if ((CERT_V1 == dwVersion && CERT_V1 == pksT->dwVersion) ||
(CERT_V1 != dwVersion && CERT_V1 != pksT->dwVersion))
{
wprintf(L"@del ");
DumpGetRecoverMergeCommandLine(
NULL, // strConfig
FALSE, // fRecoverKey
pksT,
NULL, // pwszPassword
wszRECSUFFIX,
NULL); // ppwszSimpleName
wprintf(wszNewLine);
wprintf(L"@del ");
DumpGetRecoverMergeCommandLine(
NULL, // strConfig
FALSE, // fRecoverKey
pksT,
NULL, // pwszPassword
wszP12SUFFIX,
NULL); // ppwszSimpleName
wprintf(wszNewLine);
}
}
if (CERT_V1 == dwVersion)
{
// generate certutil -ConvertEPF command:
wprintf(
L"%ws -p \"%ws,%ws\" -ConvertEPF \"%ws%ws%ws\" \"%ws%ws\"\n",
g_pwszProg,
wszBatchPassword(0, pwszPasswordIntermediate),
wszBatchPassword(1, pwszPasswordFinal),
pwszSimpleName,
wszV1SUFFIX,
wszP12SUFFIX,
pwszSimpleName,
wszEPFSUFFIX);
wszBatchPassword(0, NULL); // password data
wszBatchPassword(1, NULL); // password data
// generate V1 intermediate PFX file delete command:
wprintf(L"@del ");
wprintf(
L"@delete \"%ws%ws%ws\"\n",
pwszSimpleName,
wszV1SUFFIX,
wszP12SUFFIX);
}
//error:
if (NULL != pwszSimpleName)
{
LocalFree(pwszSimpleName);
}
}
HRESULT
cuGenerateOutFilePassword(
OUT WCHAR **ppwszPassword)
{
HRESULT hr;
WCHAR wszPassword[MAX_PATH];
*ppwszPassword = NULL;
hr = cuGeneratePassword(
1, // cwcMax (use default length)
wszPassword,
ARRAYSIZE(wszPassword));
hr = myDupString(wszPassword, ppwszPassword);
_JumpIfError(hr, error, "myDupString");
error:
SecureZeroMemory(wszPassword, sizeof(wszPassword)); // password data
return(hr);
}
HRESULT
verbGetKey(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszUserNameOrSerialNumber,
OPTIONAL IN WCHAR const *pwszfnRecoveryBlob,
IN WCHAR const *pwszArg3,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
DISPATCHINTERFACE diConfig;
BOOL fMustRelease = FALSE;
WCHAR *pwszCommonName = NULL;
WCHAR const *pwszRequesterName = NULL;
WCHAR const *pwszUPN = NULL;
BSTR strConfig = NULL;
BSTR strSerialNumber = NULL;
BYTE *pbHash = NULL;
DWORD cbHash;
BSTR strHash = NULL;
GETKEYSERIAL *pksList = NULL;
GETKEYSERIAL *pksT;
DWORD cCandidate;
DWORD cCandidateV1;
DWORD cCandidateV3;
WCHAR *pwszPasswordIntermediate = NULL;
WCHAR *pwszPasswordFinal = NULL;
if (NULL == pwszfnRecoveryBlob)
{
wprintf(L"\n@goto start\n");
}
hr = myMakeSerialBstr(pwszUserNameOrSerialNumber, &strSerialNumber);
CSASSERT((S_OK != hr) ^ (NULL != strSerialNumber));
hr = WszToMultiByteInteger(
TRUE,
pwszUserNameOrSerialNumber,
&cbHash,
&pbHash);
_PrintIfError2(hr, "WszToMultiByteInteger", hr);
if (S_OK == hr)
{
hr = MultiByteIntegerToBstr(TRUE, cbHash, pbHash, &strHash);
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
}
hr = myDupString(pwszUserNameOrSerialNumber, &pwszCommonName);
_JumpIfError(hr, error, "myDupString");
cuConvertEscapeSequences(pwszCommonName);
if (NULL != wcschr(pwszUserNameOrSerialNumber, L'\\'))
{
pwszRequesterName = pwszUserNameOrSerialNumber;
}
if (NULL != wcschr(pwszUserNameOrSerialNumber, L'@'))
{
pwszUPN = pwszUserNameOrSerialNumber;
}
if (NULL == g_pwszConfig)
{
LONG i;
LONG count;
LONG Index;
hr = Config_Init(g_DispatchFlags, &diConfig);
_JumpIfError(hr, error, "Config_Init");
fMustRelease = TRUE;
hr = Config_Reset(&diConfig, 0, &count);
_JumpIfError(hr, error, "Config_Reset");
Index = 0;
for (i = 0; i < count; i++)
{
hr = Config_Next(&diConfig, &Index);
if (S_OK != hr && S_FALSE != hr)
{
_JumpError(hr, error, "Config_Next");
}
hr = S_OK;
if (-1 == Index)
{
break;
}
hr = Config_GetField(&diConfig, wszCONFIG_CONFIG, &strConfig);
_JumpIfError(hr, error, "Config_GetField");
hr = GetKey(
strConfig,
pwszCommonName,
pwszRequesterName,
pwszUPN,
strSerialNumber,
strHash,
&pksList);
_PrintIfError(hr, "GetKey"); // Ignore connection failures
}
}
else
{
hr = GetKey(
g_pwszConfig,
pwszCommonName,
pwszRequesterName,
pwszUPN,
strSerialNumber,
strHash,
&pksList);
_JumpIfError(hr, error, "GetKey");
}
cCandidateV1 = 0;
cCandidateV3 = 0;
for (pksT = pksList; NULL != pksT; pksT = pksT->Next)
{
if (NULL != pksT->strConfig)
{
wprintf(L"\n\"%ws\"\n", pksT->strConfig);
}
hr = DumpRecoveryCandidate(pksT);
_JumpIfError(hr, error, "DumpRecoveryCandidate");
if (CERT_V1 == pksT->dwVersion)
{
cCandidateV1++;
}
else
{
cCandidateV3++;
}
}
cCandidate = cCandidateV1 + cCandidateV3;
if (NULL == pwszfnRecoveryBlob && 0 != cCandidate)
{
hr = cuGenerateOutFilePassword(&pwszPasswordIntermediate);
_JumpIfError(hr, error, "cuGenerateOutFilePassword");
hr = cuGenerateOutFilePassword(&pwszPasswordFinal);
_JumpIfError(hr, error, "cuGenerateOutFilePassword");
wprintf(L"\n:start\n");
if (0 != cCandidateV1)
{
DumpRecoveryCommandLines(
pksList,
CERT_V1,
cCandidateV1,
pwszPasswordIntermediate,
pwszPasswordFinal);
}
if (0 != cCandidateV3)
{
DumpRecoveryCommandLines(
pksList,
CERT_V3,
cCandidateV3,
pwszPasswordIntermediate,
pwszPasswordFinal);
}
wprintf(L"@echo PASSWORD: \"%ws\"\n", wszBatchPassword(0, pwszPasswordFinal));
wszBatchPassword(0, NULL); // password data
wprintf(L"\n@goto exit\n");
}
if (1 != cCandidate)
{
hr = 0 == cCandidate? CRYPT_E_NOT_FOUND : TYPE_E_AMBIGUOUSNAME;
_JumpError(hr, error, "GetKey");
}
hr = GetArchivedKey(
pksList->strConfig,
pksList->RequestId,
pwszfnRecoveryBlob);
_JumpIfError(hr, error, "GetArchivedKey");
error:
while (NULL != pksList)
{
pksT = pksList;
pksList = pksList->Next;
FreeKeySerialEntry(pksT);
}
if (fMustRelease)
{
Config_Release(&diConfig);
}
if (NULL != strConfig)
{
SysFreeString(strConfig);
}
if (NULL != pwszPasswordIntermediate)
{
myZeroDataString(pwszPasswordIntermediate); // password data
LocalFree(pwszPasswordIntermediate);
}
if (NULL != pwszPasswordFinal)
{
myZeroDataString(pwszPasswordFinal); // password data
LocalFree(pwszPasswordFinal);
}
if (NULL != pwszCommonName)
{
LocalFree(pwszCommonName);
}
if (NULL != pbHash)
{
LocalFree(pbHash);
}
if (NULL != strHash)
{
SysFreeString(strHash);
}
if (NULL != strSerialNumber)
{
SysFreeString(strSerialNumber);
}
return(hr);
}
VOID
DeleteKey(
IN CRYPT_KEY_PROV_INFO const *pkpi)
{
HCRYPTPROV hProv;
CryptAcquireContext(
&hProv,
pkpi->pwszContainerName,
pkpi->pwszProvName,
pkpi->dwProvType,
CRYPT_DELETEKEYSET | pkpi->dwFlags);
}
HRESULT
SaveRecoveredKey(
IN CERT_CONTEXT const *pccUser,
IN BYTE const *pbKey,
IN DWORD cbKey,
OPTIONAL IN WCHAR const *pwszfnPFX,
OPTIONAL IN WCHAR const *pwszPassword)
{
HRESULT hr;
HCERTSTORE hStoreMemory = NULL;
BOOL fMatchingKey;
WCHAR wszPassword[MAX_PATH];
CRYPT_KEY_PROV_INFO kpi;
CRYPT_DATA_BLOB pfx;
BOOL fSigningKey;
pfx.pbData = NULL;
ZeroMemory(&kpi, sizeof(kpi));
hr = myValidateKeyBlob(
pbKey,
cbKey,
&pccUser->pCertInfo->SubjectPublicKeyInfo,
CERT_V1 == pccUser->pCertInfo->dwVersion,
&fSigningKey,
&kpi);
_JumpIfError(hr, error, "myValidateKeyBlob");
if (!CertSetCertificateContextProperty(
pccUser,
CERT_KEY_PROV_INFO_PROP_ID,
0,
&kpi))
{
hr = myHLastError();
_JumpError(hr, error, "CertSetCertificateContextProperty");
}
hStoreMemory = CertOpenStore(
CERT_STORE_PROV_MEMORY,
X509_ASN_ENCODING,
NULL,
0,
NULL);
if (NULL == hStoreMemory)
{
hr = myHLastError();
_JumpError(hr, error, "CertOpenStore");
}
// Begin Chain Building
hr = myAddChainToMemoryStore(hStoreMemory, pccUser, g_dwmsTimeout);
_JumpIfError(hr, error, "myAddChainToMemoryStore");
// End Chain Building
if (NULL != pwszfnPFX)
{
hr = cuGetPassword(
0, // idsPrompt
NULL, // pwszfn
pwszPassword,
TRUE, // fVerify
wszPassword,
ARRAYSIZE(wszPassword),
&pwszPassword);
_JumpIfError(hr, error, "cuGetPassword");
}
hr = myPFXExportCertStore(
hStoreMemory,
&pfx,
pwszPassword,
!g_fWeakPFX,
EXPORT_PRIVATE_KEYS | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY);
_JumpIfError(hr, error, "myPFXExportCertStore");
if (NULL != pwszfnPFX)
{
hr = EncodeToFileW(
pwszfnPFX,
pfx.pbData,
pfx.cbData,
CRYPT_STRING_BINARY | (g_fForce? DECF_FORCEOVERWRITE : 0));
_JumpIfError(hr, error, "EncodeToFileW");
}
error:
SecureZeroMemory(wszPassword, sizeof(wszPassword)); // password data
if (NULL != hStoreMemory)
{
CertCloseStore(hStoreMemory, CERT_CLOSE_STORE_CHECK_FLAG);
}
if (NULL != kpi.pwszContainerName)
{
DeleteKey(&kpi);
LocalFree(kpi.pwszContainerName);
}
if (NULL != pfx.pbData)
{
LocalFree(pfx.pbData);
}
return(hr);
}
#if DBG_CERTSRV
#define CDWMS 6
VOID
DumpRecoverTime(
IN char const *pszPrefix,
IN DWORD *rgdwms,
IN DWORD idwms)
{
CSASSERT(0 < idwms);
CSASSERT(CDWMS > idwms);
rgdwms[idwms] = GetTickCount();
DBGPRINT((
g_fVerbose? DBG_SS_CERTUTIL : DBG_SS_CERTUTILI,
"RecoverKey[%u]: %hs: %ums/%ums\n",
idwms,
pszPrefix,
rgdwms[idwms] - rgdwms[idwms - 1],
rgdwms[idwms] - rgdwms[0]));
}
#endif
HRESULT
verbRecoverKey(
IN WCHAR const *pwszOption,
IN WCHAR const *pwszfnRecoveryBlob,
OPTIONAL IN WCHAR const *pwszfnPFX,
OPTIONAL IN WCHAR const *pwszRecipientIndex,
IN WCHAR const *pwszArg4)
{
HRESULT hr;
BYTE *pbIn = NULL;
DWORD cbIn;
BYTE *pbEncryptedPKCS7 = NULL;
DWORD cbEncryptedPKCS7;
DWORD cSigner;
DWORD cRecipient;
DWORD dwMsgType;
char *pszInnerContentObjId = NULL;
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
CERT_CONTEXT const *pccUser = NULL;
BYTE abHashUserCert[CBMAX_CRYPT_HASH_LEN];
CRYPT_HASH_BLOB BlobHash;
BYTE *pbKey = NULL;
DWORD cbKey;
DWORD RecipientIndex = MAXDWORD;
DBGCODE(DWORD adwms[CDWMS]);
DBGCODE(adwms[0] = GetTickCount());
if (NULL != pwszRecipientIndex)
{
hr = myGetLong(pwszRecipientIndex, (LONG *) &RecipientIndex);
_JumpIfError(hr, error, "RecipientIndex must be a number");
}
hr = DecodeFileW(pwszfnRecoveryBlob, &pbIn, &cbIn, CRYPT_STRING_ANY);
if (S_OK != hr)
{
cuPrintError(IDS_ERR_FORMAT_DECODEFILE, hr);
goto error;
}
// Decode outer PKCS 7 signed message, which contains all of the certs.
hr = myDecodePKCS7(
pbIn,
cbIn,
&pbEncryptedPKCS7,
&cbEncryptedPKCS7,
&dwMsgType,
&pszInnerContentObjId,
&cSigner,
&cRecipient,
&hStore,
&hMsg);
_JumpIfError(hr, error, "myDecodePKCS7(outer)");
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
if (CMSG_SIGNED != dwMsgType)
{
_JumpError(hr, error, "dwMsgType(outer)");
}
if (0 == cSigner)
{
_JumpError(hr, error, "cSigner(outer)");
}
if (0 != cRecipient)
{
_JumpError(hr, error, "cRecipient(outer)");
}
if (NULL == pszInnerContentObjId ||
0 != strcmp(szOID_RSA_data, pszInnerContentObjId))
{
_JumpError(hr, error, "pszInnerContentObjId(outer)");
}
CSASSERT(NULL != hMsg);
ZeroMemory(abHashUserCert, sizeof(abHashUserCert));
BlobHash.cbData = sizeof(abHashUserCert);
BlobHash.pbData = abHashUserCert;
hr = cuDumpSigners(
hMsg,
pszInnerContentObjId,
hStore,
cSigner,
NULL == pbEncryptedPKCS7, // fContentEmpty
TRUE, // fVerifyOnly
BlobHash.pbData,
&BlobHash.cbData);
_JumpIfError(hr, error, "cuDumpSigners(outer)");
pccUser = CertFindCertificateInStore(
hStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0, // dwFindFlags
CERT_FIND_HASH,
&BlobHash,
NULL);
if (NULL == pccUser)
{
hr = myHLastError();
_JumpError(hr, error, "CertFindCertificateInStore");
}
LocalFree(pszInnerContentObjId);
pszInnerContentObjId = NULL;
CryptMsgClose(hMsg);
hMsg = NULL;
// Decode inner PKCS 7 encrypted message, which contains the private key.
DBGCODE(DumpRecoverTime("Outer PKCS 7", adwms, 1));
hr = myDecodePKCS7(
pbEncryptedPKCS7,
cbEncryptedPKCS7,
NULL, // ppbContents
NULL, // pcbContents
&dwMsgType,
&pszInnerContentObjId,
&cSigner,
&cRecipient,
NULL, // phStore
&hMsg);
_JumpIfError(hr, error, "myDecodePKCS7(inner)");
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
if (CMSG_ENVELOPED != dwMsgType)
{
_JumpError(hr, error, "dwMsgType(inner)");
}
if (0 != cSigner)
{
_JumpError(hr, error, "cSigner(inner)");
}
if (0 == cRecipient)
{
_JumpError(hr, error, "cRecipient(inner)");
}
if (NULL == pszInnerContentObjId ||
0 != strcmp(szOID_RSA_data, pszInnerContentObjId))
{
_JumpError(hr, error, "pszInnerContentObjId(inner)");
}
CSASSERT(NULL != hMsg);
if (MAXDWORD != RecipientIndex && cRecipient <= RecipientIndex)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "RecipientIndex too large");
}
DBGCODE(DumpRecoverTime("Inner PKCS 7", adwms, 2));
hr = cuDumpEncryptedAsnBinary(
hMsg,
cRecipient,
RecipientIndex,
hStore,
NULL,
pbEncryptedPKCS7,
cbEncryptedPKCS7,
TRUE,
&pbKey,
&cbKey);
{
HRESULT hr2;
wprintf(wszNewLine);
wprintf(myLoadResourceString(IDS_USER_CERT)); // "User Certificate:"
wprintf(wszNewLine);
hr2 = cuDumpIssuerSerialAndSubject(
&pccUser->pCertInfo->Issuer,
&pccUser->pCertInfo->SerialNumber,
&pccUser->pCertInfo->Subject,
NULL); // hStore
_PrintIfError(hr2, "cuDumpIssuerSerialAndSubject(user)");
hr2 = cuDisplayHash(
g_wszPad4,
pccUser,
NULL,
CERT_SHA1_HASH_PROP_ID,
L"sha1");
_PrintIfError(hr2, "cuDisplayHash");
}
DBGCODE(DumpRecoverTime("Decrypt key", adwms, 3));
if (CRYPT_E_NO_DECRYPT_CERT != hr)
{
_JumpIfError(hr, error, "cuDumpEncryptedAsnBinary");
if (g_fVerbose)
{
wprintf(wszNewLine);
hr = cuDumpPrivateKeyBlob(pbKey, cbKey, FALSE);
_JumpIfError(hr, error, "cuDumpPrivateKeyBlob");
}
// Verify the key matches the cert, then save in a PFX
hr = SaveRecoveredKey(
pccUser,
pbKey,
cbKey,
pwszfnPFX,
g_pwszPassword);
_JumpIfError(hr, error, "SaveRecoveredKey");
DBGCODE(DumpRecoverTime("Save key", adwms, 4));
}
else
{
// Can't decrypt the private key, list Recipient cert info.
wprintf(myLoadResourceString(IDS_CANT_DECRYPT)); // "Cannot decrypt message content."
wprintf(wszNewLine);
DBGCODE(DumpRecoverTime("nop", adwms, 4));
}
if (CRYPT_E_NO_DECRYPT_CERT == hr || NULL == pwszfnPFX)
{
HRESULT hrDecrypt = hr;
wprintf(wszNewLine);
wprintf(myLoadResourceString(IDS_NEED_RECOVERY_CERT)); // "Key recovery requires one of the following certificates and its private key:"
wprintf(wszNewLine);
hr = cuDumpRecipients(hMsg, hStore, cRecipient, TRUE);
_JumpIfError(hr, error, "cuDumpRecipients");
hr = hrDecrypt;
_JumpIfError(hr, error, "Cannot decrypt");
}
DBGCODE(DumpRecoverTime("Done", adwms, 5));
hr = S_OK;
error:
if (NULL != pccUser)
{
CertFreeCertificateContext(pccUser);
}
if (NULL != pbKey)
{
LocalFree(pbKey);
}
if (NULL != pbIn)
{
LocalFree(pbIn);
}
if (NULL != pbEncryptedPKCS7)
{
LocalFree(pbEncryptedPKCS7);
}
if (NULL != pszInnerContentObjId)
{
LocalFree(pszInnerContentObjId);
}
if (NULL != hStore)
{
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
}
if (NULL != hMsg)
{
CryptMsgClose(hMsg);
}
return(hr);
}