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.
4955 lines
121 KiB
4955 lines
121 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1995 - 1999
|
|
//
|
|
// File: initlib.cpp
|
|
//
|
|
// Contents: Install cert server
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include <pch.cpp>
|
|
|
|
#pragma hdrstop
|
|
|
|
// C Run-Time Includes
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <memory.h>
|
|
#include <time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <io.h>
|
|
#include <winldap.h>
|
|
#include <ntldap.h>
|
|
|
|
// Windows System Includes
|
|
#include <winsvc.h>
|
|
#include <rpc.h>
|
|
#include <tchar.h>
|
|
#include <lmaccess.h>
|
|
#include <lmwksta.h>
|
|
#include <csdisp.h>
|
|
#include <wincrypt.h>
|
|
#include <objbase.h>
|
|
#include <initguid.h>
|
|
#include <userenv.h>
|
|
#include <cainfop.h>
|
|
|
|
|
|
#define SECURITY_WIN32
|
|
#include <security.h>
|
|
|
|
#include <lmerr.h>
|
|
|
|
// Application Includes
|
|
#include "setupids.h"
|
|
#include "certmsg.h"
|
|
#include "certca.h"
|
|
#include "certhier.h"
|
|
#include "tfc.h"
|
|
#include "cscsp.h"
|
|
#include "csldap.h"
|
|
#include "certacl.h"
|
|
|
|
#define __dwFILE__ __dwFILE_INITLIB_INITLIB_CPP__
|
|
|
|
WCHAR const g_szSlash[] = L"\\";
|
|
DWORD g_dwNameEncodeFlags = CERT_RDN_ENABLE_UTF8_UNICODE_FLAG;
|
|
|
|
#define MAX_COMPUTER_DNS_NAME 256
|
|
|
|
using namespace CertSrv;
|
|
|
|
|
|
//+=====================================================================
|
|
// DS DNs:
|
|
//
|
|
// DomainDN Example (no longer used for Cert server DS objects):
|
|
// DC=pksdom2,DC=nttest,DC=microsoft,DC=com
|
|
//
|
|
// ConfigDN Example:
|
|
// CN=Configuration,DC=pksdom2,DC=nttest,DC=microsoft,DC=com
|
|
//
|
|
// Cert server DS objects reside in Public Key Services container under
|
|
// the Configuraton container:
|
|
// CN=Public Key Services,CN=Services,<ConfigDN>
|
|
//
|
|
//
|
|
// In the Public Key Services container:
|
|
//
|
|
// Root Trust container:
|
|
// Each Root CA creates a Root Trust object in this container to store trusted
|
|
// Root CA certificates downloaded by all DS clients.
|
|
// Renewed CAs and CAs on multiple machines using the same CA name may use the
|
|
// same Root Trust object, because certs are always added -- they are never
|
|
// removed.
|
|
//
|
|
// CN=Certification Authorities
|
|
// CN=CA foo
|
|
// CN=CA bar
|
|
// ...
|
|
//
|
|
//
|
|
// Authority Information Access container:
|
|
// Each CA creates an AIA object in this container to store CA certs for chain
|
|
// building. Renewed CAs and CAs on multiple machines using the same CA name
|
|
// may use the same AIA object, because certs are always added -- they are
|
|
// never removed.
|
|
//
|
|
// CN=AIA
|
|
// CN=CA foo
|
|
// CN=CA bar
|
|
// ...
|
|
//
|
|
//
|
|
// CRL Distribution Point containers:
|
|
// Each CA creates a CDP object in this container for each unique CA key to
|
|
// store CRLs for revocation checking. Only one base CRL and zero or one
|
|
// delta CRL are stored in each CDP object, due to potential size constraints,
|
|
// and because the attribute is single valued. When a CA is renewed and a new
|
|
// CA key is generated during the renewal, a new CDP object is created with
|
|
// the CA's key index (in parentheses) appended to the CN. A nested container
|
|
// is created for each machine with the CN set to the short machine name
|
|
// (first component of the machine's full DNS name).
|
|
//
|
|
// CN=CDP
|
|
// CN=<CA foo's MachineName>
|
|
// CN=CA foo
|
|
// CN=CA foo(1)
|
|
// CN=CA foo(3)
|
|
// CN=<CA bar's MachineName>
|
|
// CN=CA bar
|
|
// CN=CA bar(1)
|
|
//
|
|
//
|
|
// Enrollment Services container:
|
|
// Each CA creates an Enrollment Services object in this container. A flags
|
|
// attribute indicates whether the CA supports autoenrollment (an Enterprise
|
|
// CA) or not (Standalone CA). The Enrollment Services object publishes the
|
|
// existence of the CA to all DS clients. Enrollment Services objects are
|
|
// created and managed by the certca.h CA APIs.
|
|
//
|
|
// CN=Enrollment Services
|
|
// CN=CA foo
|
|
// CN=CA bar
|
|
// ...
|
|
//
|
|
// Enterprise Trust object:
|
|
// A single Enterprise Trust object contains certificates for all
|
|
// autoenrollment-enabled CAs (root and subordinate Entrprise CAs).
|
|
//
|
|
// CN=NTAuthCertificates
|
|
//
|
|
//======================================================================
|
|
|
|
|
|
WCHAR const s_wszRootCAs[] =
|
|
L","
|
|
L"CN=Certification Authorities,"
|
|
L"CN=Public Key Services,"
|
|
L"CN=Services,";
|
|
|
|
WCHAR const s_wszEnterpriseCAs[] =
|
|
L"CN=NTAuthCertificates,"
|
|
L"CN=Public Key Services,"
|
|
L"CN=Services,";
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Write an encoded DER blob to a file
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
csiWriteDERToFile(
|
|
IN WCHAR const *pwszFileName,
|
|
IN BYTE const *pbDER,
|
|
IN DWORD cbDER,
|
|
IN HINSTANCE hInstance,
|
|
IN BOOL fUnattended,
|
|
IN HWND hwnd)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
HRESULT hr;
|
|
HANDLE hLocalFile;
|
|
DWORD dwBytesWritten;
|
|
|
|
hr = S_OK;
|
|
|
|
// Write the Encoded Blob to the file
|
|
hLocalFile = CreateFile(
|
|
pwszFileName,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
0,
|
|
0);
|
|
if (INVALID_HANDLE_VALUE == hLocalFile)
|
|
{
|
|
hr = myHLastError();
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_CREATEFILE,
|
|
hr,
|
|
pwszFileName);
|
|
_JumpError(hr, error, "CreateFile");
|
|
}
|
|
|
|
if (!WriteFile(hLocalFile, pbDER, cbDER, &dwBytesWritten, NULL))
|
|
{
|
|
hr = myHLastError();
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_WRITEFILE,
|
|
hr,
|
|
pwszFileName);
|
|
_JumpError(hr, error, "WriteFile");
|
|
}
|
|
fResult = TRUE;
|
|
|
|
error:
|
|
if (INVALID_HANDLE_VALUE != hLocalFile)
|
|
{
|
|
CloseHandle(hLocalFile);
|
|
}
|
|
if (!fResult)
|
|
{
|
|
SetLastError(hr);
|
|
}
|
|
return(fResult);
|
|
}
|
|
|
|
|
|
BOOL
|
|
CreateKeyUsageExtension(
|
|
BYTE bIntendedKeyUsage,
|
|
OUT BYTE **ppbEncoded,
|
|
IN OUT DWORD *pcbEncoded,
|
|
HINSTANCE hInstance,
|
|
BOOL fUnattended,
|
|
HWND hwnd)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
HRESULT hr;
|
|
BYTE *pbEncoded = NULL;
|
|
DWORD cbEncoded;
|
|
CRYPT_BIT_BLOB KeyUsage;
|
|
|
|
KeyUsage.pbData = &bIntendedKeyUsage;
|
|
KeyUsage.cbData = 1;
|
|
KeyUsage.cUnusedBits = 0;
|
|
|
|
hr = S_OK;
|
|
if (!myEncodeKeyUsage(
|
|
X509_ASN_ENCODING,
|
|
&KeyUsage,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pbEncoded,
|
|
&cbEncoded))
|
|
{
|
|
hr = myHLastError();
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_ENCODEKEYATTR,
|
|
hr,
|
|
NULL);
|
|
cbEncoded = 0;
|
|
goto error;
|
|
}
|
|
fResult = TRUE;
|
|
|
|
error:
|
|
if (!fResult)
|
|
{
|
|
SetLastError(hr);
|
|
}
|
|
*ppbEncoded = pbEncoded;
|
|
*pcbEncoded = cbEncoded;
|
|
return(fResult);
|
|
}
|
|
|
|
|
|
#ifdef USE_NETSCAPE_TYPE_EXTENSION
|
|
BOOL
|
|
CreateNetscapeTypeExtension(
|
|
OUT BYTE **ppbEncoded,
|
|
OUT DWORD *pcbEncoded)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
CRYPT_BIT_BLOB NetscapeType;
|
|
BYTE temp = NETSCAPE_SSL_CA_CERT_TYPE | NETSCAPE_SMIME_CA_CERT_TYPE;
|
|
|
|
NetscapeType.pbData = &temp;
|
|
NetscapeType.cbData = 1;
|
|
NetscapeType.cUnusedBits = 0;
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_BITS,
|
|
&NetscapeType,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
ppbEncoded,
|
|
pcbEncoded))
|
|
{
|
|
goto exit;
|
|
}
|
|
fResult = TRUE;
|
|
|
|
exit:
|
|
return(fResult);
|
|
}
|
|
#endif
|
|
|
|
|
|
HRESULT
|
|
GetRegCRLDistributionPoints(
|
|
IN WCHAR const *pwszSanitizedName,
|
|
OUT WCHAR **ppwszz)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszz = NULL;
|
|
WCHAR *pwsz;
|
|
WCHAR *pwszzCopy;
|
|
DWORD cwc;
|
|
DWORD Flags;
|
|
WCHAR *pwszT;
|
|
|
|
*ppwszz = NULL;
|
|
hr = myGetCertRegMultiStrValue(
|
|
pwszSanitizedName,
|
|
NULL,
|
|
NULL,
|
|
wszREGCRLPUBLICATIONURLS,
|
|
&pwszz);
|
|
|
|
if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
|
|
{
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
_JumpIfErrorStr(
|
|
hr,
|
|
error,
|
|
"myGetCertRegMultiStrValue",
|
|
wszREGCRLPUBLICATIONURLS);
|
|
cwc = 0;
|
|
for (pwsz = pwszz; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1)
|
|
{
|
|
Flags = _wtoi(pwsz);
|
|
if (CSURL_ADDTOCERTCDP & Flags)
|
|
{
|
|
pwszT = pwsz;
|
|
while (iswdigit(*pwszT))
|
|
{
|
|
pwszT++;
|
|
}
|
|
if (pwszT > pwsz && L':' == *pwszT)
|
|
{
|
|
pwszT++;
|
|
cwc += wcslen(pwszT) + 1;
|
|
}
|
|
}
|
|
}
|
|
if (0 == cwc)
|
|
{
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
|
|
pwszzCopy = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR));
|
|
if (NULL == pwszzCopy)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
*ppwszz = pwszzCopy;
|
|
|
|
for (pwsz = pwszz; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1)
|
|
{
|
|
Flags = _wtoi(pwsz);
|
|
if (CSURL_ADDTOCERTCDP & Flags)
|
|
{
|
|
pwszT = pwsz;
|
|
while (iswdigit(*pwszT))
|
|
{
|
|
pwszT++;
|
|
}
|
|
if (pwszT > pwsz && L':' == *pwszT)
|
|
{
|
|
pwszT++;
|
|
wcscpy(pwszzCopy, pwszT);
|
|
pwszzCopy += wcslen(pwszT) + 1;
|
|
}
|
|
}
|
|
}
|
|
*pwszzCopy = L'\0';
|
|
CSASSERT(SAFE_SUBTRACT_POINTERS(pwszzCopy, *ppwszz) == cwc);
|
|
|
|
error:
|
|
if (NULL != pwszz)
|
|
{
|
|
LocalFree(pwszz);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
FormatTemplateURLs(
|
|
IN WCHAR const *pwszSanitizedName,
|
|
IN DWORD iCert,
|
|
IN DWORD iCRL,
|
|
IN BOOL fUseDS,
|
|
IN WCHAR const *pwszzIn,
|
|
OUT DWORD *pcpwsz,
|
|
OUT WCHAR ***ppapwszOut)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
WCHAR const **papwszTemplate = NULL;
|
|
WCHAR const *pwsz;
|
|
WCHAR **papwszOut = NULL;
|
|
DWORD cpwsz = 0;
|
|
WCHAR *pwszServerName = NULL;
|
|
LDAP *pld = NULL;
|
|
BSTR strConfigDN = NULL;
|
|
BSTR strDomainDN = NULL;
|
|
|
|
*pcpwsz = 0;
|
|
*ppapwszOut = NULL;
|
|
|
|
cpwsz = 0;
|
|
if (NULL != pwszzIn)
|
|
{
|
|
for (pwsz = pwszzIn; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1)
|
|
{
|
|
cpwsz++;
|
|
}
|
|
}
|
|
if (0 == cpwsz)
|
|
{
|
|
hr = S_FALSE;
|
|
goto error;
|
|
}
|
|
|
|
papwszTemplate = (WCHAR const **) LocalAlloc(
|
|
LMEM_FIXED,
|
|
cpwsz * sizeof(papwszTemplate[0]));
|
|
if (NULL == papwszTemplate)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
i = 0;
|
|
for (pwsz = pwszzIn; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1)
|
|
{
|
|
papwszTemplate[i++] = pwsz;
|
|
}
|
|
CSASSERT(i == cpwsz);
|
|
|
|
papwszOut = (WCHAR **) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cpwsz * sizeof(papwszOut[0]));
|
|
if (NULL == papwszOut)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
hr = myGetMachineDnsName(&pwszServerName);
|
|
_JumpIfError(hr, error, "myGetMachineDnsName");
|
|
|
|
// bind to ds -- even for !fUseDS, just in case the URLs need to domain DN
|
|
|
|
hr = myLdapOpen(
|
|
NULL, // pwszDomainName
|
|
RLBF_REQUIRE_SECURE_LDAP, // dwFlags
|
|
&pld,
|
|
&strDomainDN,
|
|
&strConfigDN);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "myLdapOpen");
|
|
if (fUseDS)
|
|
{
|
|
_JumpError(hr, error, "myLdapOpen");
|
|
}
|
|
strDomainDN = SysAllocString(L"");
|
|
strConfigDN = SysAllocString(L"");
|
|
if (NULL == strDomainDN || NULL == strConfigDN)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "SysAllocString");
|
|
}
|
|
}
|
|
hr = myFormatCertsrvStringArray(
|
|
TRUE, // fURL
|
|
pwszServerName, // pwszServerName_p1_2
|
|
pwszSanitizedName, // pwszSanitizedName_p3_7
|
|
iCert, // iCert_p4
|
|
MAXDWORD, // iCertTarget_p4
|
|
strDomainDN, // pwszDomainDN_p5
|
|
strConfigDN, // pwszConfigDN_p6
|
|
iCRL, // iCRL_p8
|
|
FALSE, // fDeltaCRL_p9
|
|
TRUE, // fDSAttrib_p10_11
|
|
cpwsz, // cStrings
|
|
papwszTemplate, // apwszStringsIn
|
|
papwszOut); // apwszStringsOut
|
|
_JumpIfError(hr, error, "myFormatCertsrvStringArray");
|
|
|
|
*pcpwsz = cpwsz;
|
|
*ppapwszOut = papwszOut;
|
|
papwszOut = NULL;
|
|
|
|
error:
|
|
if (NULL != pwszServerName)
|
|
{
|
|
LocalFree(pwszServerName);
|
|
}
|
|
myLdapClose(pld, strDomainDN, strConfigDN);
|
|
if (NULL != papwszTemplate)
|
|
{
|
|
LocalFree(papwszTemplate);
|
|
}
|
|
if (NULL != papwszOut)
|
|
{
|
|
for (i = 0; i < cpwsz; i++)
|
|
{
|
|
if (papwszOut[i])
|
|
{
|
|
LocalFree(papwszOut[i]);
|
|
}
|
|
}
|
|
LocalFree(papwszOut);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CreateRevocationExtension
|
|
//
|
|
// Return S_OK if extension has been constructed.
|
|
// Return S_FALSE if empty section detected in INF file
|
|
// Return other error if no section detected in INF file
|
|
//+--------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CreateRevocationExtension(
|
|
IN HINF hInf,
|
|
IN WCHAR const *pwszSanitizedName,
|
|
IN DWORD iCert,
|
|
IN DWORD iCRL,
|
|
IN BOOL fUseDS,
|
|
IN DWORD dwRevocationFlags,
|
|
OUT BOOL *pfCritical,
|
|
OUT BYTE **ppbEncoded,
|
|
OUT DWORD *pcbEncoded)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
WCHAR *pwszzCDP = NULL;
|
|
WCHAR **papwszURL = NULL;
|
|
CRL_DIST_POINTS_INFO CRLDistInfo;
|
|
CRL_DIST_POINT CRLDistPoint;
|
|
CERT_ALT_NAME_INFO *pAltInfo;
|
|
|
|
ZeroMemory(&CRLDistPoint, sizeof(CRLDistPoint));
|
|
pAltInfo = &CRLDistPoint.DistPointName.FullName;
|
|
|
|
*ppbEncoded = NULL;
|
|
*pcbEncoded = 0;
|
|
|
|
hr = E_HANDLE;
|
|
if (INVALID_HANDLE_VALUE != hInf)
|
|
{
|
|
hr = myInfGetCRLDistributionPoints(hInf, pfCritical, &pwszzCDP);
|
|
csiLogInfError(hInf, hr);
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
if (S_FALSE == hr)
|
|
{
|
|
_JumpError2(hr, error, "myInfGetCRLDistributionPoints", hr);
|
|
}
|
|
hr = GetRegCRLDistributionPoints(
|
|
pwszSanitizedName,
|
|
&pwszzCDP);
|
|
_JumpIfError(hr, error, "GetRegCRLDistributionPoints");
|
|
}
|
|
|
|
if (0 == (REVEXT_CDPENABLE & dwRevocationFlags))
|
|
{
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
hr = FormatTemplateURLs(
|
|
pwszSanitizedName,
|
|
iCert,
|
|
iCRL,
|
|
fUseDS,
|
|
pwszzCDP,
|
|
&pAltInfo->cAltEntry,
|
|
&papwszURL);
|
|
_JumpIfError(hr, error, "FormatTemplateURLs");
|
|
|
|
CRLDistInfo.cDistPoint = 1;
|
|
CRLDistInfo.rgDistPoint = &CRLDistPoint;
|
|
|
|
CRLDistPoint.DistPointName.dwDistPointNameChoice = CRL_DIST_POINT_FULL_NAME;
|
|
|
|
pAltInfo->rgAltEntry = (CERT_ALT_NAME_ENTRY *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
pAltInfo->cAltEntry * sizeof(pAltInfo->rgAltEntry[0]));
|
|
if (NULL == pAltInfo->rgAltEntry)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
for (i = 0; i < pAltInfo->cAltEntry; i++)
|
|
{
|
|
pAltInfo->rgAltEntry[i].pwszURL = papwszURL[i];
|
|
pAltInfo->rgAltEntry[i].dwAltNameChoice = CERT_ALT_NAME_URL;
|
|
|
|
DBGPRINT((DBG_SS_CERTLIB, "CDP[%u] = '%ws'\n", i, papwszURL[i]));
|
|
}
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_CRL_DIST_POINTS,
|
|
&CRLDistInfo,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
ppbEncoded,
|
|
pcbEncoded))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpIfError(hr, error, "myEncodeObject");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pAltInfo->rgAltEntry)
|
|
{
|
|
LocalFree(pAltInfo->rgAltEntry);
|
|
}
|
|
if (NULL != papwszURL)
|
|
{
|
|
for (i = 0; i < pAltInfo->cAltEntry; i++)
|
|
{
|
|
if (NULL != papwszURL[i])
|
|
{
|
|
LocalFree(papwszURL[i]);
|
|
}
|
|
}
|
|
LocalFree(papwszURL);
|
|
}
|
|
if (NULL != pwszzCDP)
|
|
{
|
|
LocalFree(pwszzCDP);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CreateAuthorityInformationAccessExtension
|
|
//
|
|
// Return S_OK if extension has been constructed.
|
|
// Return S_FALSE if empty section detected in INF file
|
|
// Return other error if no section detected in INF file
|
|
//+--------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CreateAuthorityInformationAccessExtension(
|
|
IN HINF hInf,
|
|
IN WCHAR const *pwszSanitizedName,
|
|
IN DWORD iCert,
|
|
IN DWORD iCRL,
|
|
IN BOOL fUseDS,
|
|
OUT BOOL *pfCritical,
|
|
OUT BYTE **ppbEncoded,
|
|
OUT DWORD *pcbEncoded)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
WCHAR *pwszzAIA = NULL;
|
|
WCHAR **papwszURL = NULL;
|
|
CERT_AUTHORITY_INFO_ACCESS caio;
|
|
|
|
caio.cAccDescr = 0;
|
|
caio.rgAccDescr = NULL;
|
|
|
|
*ppbEncoded = NULL;
|
|
*pcbEncoded = 0;
|
|
|
|
hr = E_HANDLE;
|
|
if (INVALID_HANDLE_VALUE != hInf)
|
|
{
|
|
hr = myInfGetAuthorityInformationAccess(hInf, pfCritical, &pwszzAIA);
|
|
csiLogInfError(hInf, hr);
|
|
}
|
|
_JumpIfError3(
|
|
hr,
|
|
error,
|
|
"myInfGetAuthorityInformationAccess",
|
|
E_HANDLE,
|
|
S_FALSE);
|
|
|
|
hr = FormatTemplateURLs(
|
|
pwszSanitizedName,
|
|
iCert,
|
|
iCRL,
|
|
fUseDS,
|
|
pwszzAIA,
|
|
&caio.cAccDescr,
|
|
&papwszURL);
|
|
_JumpIfError(hr, error, "FormatTemplateURLs");
|
|
|
|
caio.rgAccDescr = (CERT_ACCESS_DESCRIPTION *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
caio.cAccDescr * sizeof(CERT_ACCESS_DESCRIPTION));
|
|
if (NULL == caio.rgAccDescr)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpIfError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
for (i = 0; i < caio.cAccDescr; i++)
|
|
{
|
|
caio.rgAccDescr[i].pszAccessMethod = szOID_PKIX_CA_ISSUERS;
|
|
caio.rgAccDescr[i].AccessLocation.dwAltNameChoice = CERT_ALT_NAME_URL;
|
|
caio.rgAccDescr[i].AccessLocation.pwszURL = papwszURL[i];
|
|
|
|
DBGPRINT((DBG_SS_CERTLIB, "AIA[%u] = '%ws'\n", i, papwszURL[i]));
|
|
}
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_AUTHORITY_INFO_ACCESS,
|
|
&caio,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
ppbEncoded,
|
|
pcbEncoded))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpIfError(hr, error, "myEncodeObject");
|
|
}
|
|
|
|
error:
|
|
if (NULL != caio.rgAccDescr)
|
|
{
|
|
LocalFree(caio.rgAccDescr);
|
|
}
|
|
if (NULL != papwszURL)
|
|
{
|
|
for (i = 0; i < caio.cAccDescr; i++)
|
|
{
|
|
if (NULL != papwszURL[i])
|
|
{
|
|
LocalFree(papwszURL[i]);
|
|
}
|
|
}
|
|
LocalFree(papwszURL);
|
|
}
|
|
if (NULL != pwszzAIA)
|
|
{
|
|
LocalFree(pwszzAIA);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
FillRDN(
|
|
IN char const *pszObjId,
|
|
IN WCHAR const *pwszRDN,
|
|
IN OUT CERT_RDN *prgRDN)
|
|
{
|
|
HRESULT hr;
|
|
CERT_RDN_ATTR *prgAttr = NULL;
|
|
|
|
prgAttr = (CERT_RDN_ATTR *) LocalAlloc(LMEM_FIXED, sizeof(*prgAttr));
|
|
if (NULL == prgAttr)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
prgAttr->pszObjId = const_cast<char *>(pszObjId);
|
|
prgAttr->dwValueType = 0;
|
|
prgAttr->Value.pbData = (BYTE *) pwszRDN;
|
|
prgAttr->Value.cbData = 0;
|
|
|
|
prgRDN->cRDNAttr = 1;
|
|
prgRDN->rgRDNAttr = prgAttr;
|
|
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
csiFreeCertNameInfo(
|
|
CERT_NAME_INFO *pNameInfo)
|
|
{
|
|
DWORD iRDN;
|
|
|
|
if (NULL != pNameInfo)
|
|
{
|
|
if (NULL != pNameInfo->rgRDN)
|
|
{
|
|
for (iRDN = 0; iRDN < pNameInfo->cRDN; ++iRDN)
|
|
{
|
|
if (NULL != pNameInfo->rgRDN[iRDN].rgRDNAttr)
|
|
{
|
|
LocalFree(pNameInfo->rgRDN[iRDN].rgRDNAttr);
|
|
}
|
|
}
|
|
LocalFree(pNameInfo->rgRDN);
|
|
}
|
|
LocalFree(pNameInfo);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT
|
|
FillExtension(
|
|
IN OUT CERT_EXTENSION *pDesExt,
|
|
IN OUT DWORD *pdwIndex,
|
|
IN CERT_EXTENSION *pSrcExt)
|
|
{
|
|
CSASSERT(NULL != pDesExt && NULL != pSrcExt);
|
|
|
|
if (NULL != pSrcExt->Value.pbData && 0 != pSrcExt->Value.cbData)
|
|
{
|
|
pDesExt[*pdwIndex].pszObjId = pSrcExt->pszObjId;
|
|
pDesExt[*pdwIndex].fCritical = pSrcExt->fCritical;
|
|
pDesExt[*pdwIndex].Value = pSrcExt->Value;
|
|
++(*pdwIndex);
|
|
}
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EncodeCertAndSign(
|
|
IN HCRYPTPROV hProv,
|
|
IN CERT_INFO *pCert,
|
|
IN char const *pszAlgId,
|
|
OUT BYTE **ppbSigned,
|
|
OUT DWORD *pcbSigned,
|
|
IN HINSTANCE hInstance,
|
|
IN BOOL fUnattended,
|
|
IN HWND hwnd)
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbEncoded = NULL;
|
|
DWORD cbEncoded;
|
|
|
|
*ppbSigned = NULL;
|
|
if (!myEncodeToBeSigned(
|
|
X509_ASN_ENCODING,
|
|
pCert,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pbEncoded,
|
|
&cbEncoded))
|
|
{
|
|
hr = myHLastError();
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_ENCODETOBESIGNED,
|
|
hr,
|
|
NULL);
|
|
_JumpError(hr, error, "myEncodeToBeSigned");
|
|
}
|
|
|
|
hr = myEncodeSignedContent(
|
|
hProv,
|
|
X509_ASN_ENCODING,
|
|
pszAlgId,
|
|
pbEncoded,
|
|
cbEncoded,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
ppbSigned,
|
|
pcbSigned);
|
|
_JumpIfError(hr, error, "myEncodeSignedContent");
|
|
|
|
error:
|
|
if (NULL != pbEncoded)
|
|
{
|
|
LocalFree(pbEncoded);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EncodeCACert(
|
|
IN CASERVERSETUPINFO const *pSetupInfo,
|
|
IN HCRYPTPROV hProv,
|
|
IN const WCHAR *pwszCAType,
|
|
OUT BYTE **ppbEncoded,
|
|
OUT DWORD *pcbEncoded,
|
|
IN HINSTANCE hInstance,
|
|
IN BOOL fUnattended,
|
|
IN HWND hwnd)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
BYTE *pbSubjectEncoded = NULL;
|
|
DWORD cbSubjectEncoded = 0;
|
|
BYTE *pbIssuerEncoded = NULL;
|
|
DWORD cbIssuerEncoded = 0;
|
|
CERT_PUBLIC_KEY_INFO *pPubKey = NULL;
|
|
DWORD cbPubKey;
|
|
HINF hInf = INVALID_HANDLE_VALUE;
|
|
DWORD ErrorLine;
|
|
DWORD cExtInf = 0;
|
|
CERT_EXTENSION *rgExtInf = NULL;
|
|
DWORD cExtMerged;
|
|
CERT_EXTENSION *rgExtMerged = NULL;
|
|
|
|
CERT_EXTENSIONS *pStdExts = NULL;
|
|
CERT_EXTENSION *pAllExts = NULL;
|
|
CERT_EXTENSION extKeyUsage =
|
|
{szOID_KEY_USAGE, FALSE, 0, NULL};
|
|
CERT_EXTENSION extBasicConstraints =
|
|
{NULL, FALSE, 0, NULL};
|
|
CERT_EXTENSION extAKI =
|
|
{szOID_AUTHORITY_KEY_IDENTIFIER2, FALSE, 0, NULL};
|
|
CERT_EXTENSION extSKI =
|
|
{szOID_SUBJECT_KEY_IDENTIFIER, FALSE, 0, NULL};
|
|
CERT_EXTENSION extCDP =
|
|
{szOID_CRL_DIST_POINTS, FALSE, 0, NULL};
|
|
CERT_EXTENSION extCCDP =
|
|
{szOID_CROSS_CERT_DIST_POINTS, FALSE, 0, NULL};
|
|
CERT_EXTENSION extVersion =
|
|
{szOID_CERTSRV_CA_VERSION, FALSE, 0, NULL};
|
|
CERT_EXTENSION extPolicy =
|
|
{szOID_CERT_POLICIES, FALSE, 0, NULL};
|
|
CERT_EXTENSION extAIA =
|
|
{szOID_AUTHORITY_INFO_ACCESS, FALSE, 0, NULL};
|
|
CERT_EXTENSION extEKU =
|
|
{NULL, FALSE, 0, NULL};
|
|
#ifdef USE_NETSCAPE_TYPE_EXTENSION
|
|
CERT_EXTENSION extNetscape =
|
|
{szOID_NETSCAPE_CERT_TYPE, FALSE, 0, NULL};
|
|
#endif
|
|
DWORD cExtension;
|
|
HCERTTYPE hCertType = NULL;
|
|
DWORD i;
|
|
DWORD j;
|
|
|
|
GUID guidSerialNumber;
|
|
CERT_INFO Cert;
|
|
|
|
*ppbEncoded = NULL;
|
|
|
|
hr = myInfOpenFile(NULL, &hInf, &ErrorLine);
|
|
_PrintIfError2(
|
|
hr,
|
|
"myInfOpenFile",
|
|
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
|
|
|
|
if (INVALID_HANDLE_VALUE != hInf)
|
|
{
|
|
BOOL fUTF8;
|
|
|
|
hr = myInfGetBooleanValue(
|
|
hInf,
|
|
wszINFSECTION_CERTSERVER,
|
|
wszINFKEY_UTF8,
|
|
TRUE,
|
|
&fUTF8);
|
|
csiLogInfError(hInf, hr);
|
|
if (S_OK == hr)
|
|
{
|
|
g_dwNameEncodeFlags = fUTF8? CERT_RDN_ENABLE_UTF8_UNICODE_FLAG : 0;
|
|
}
|
|
}
|
|
|
|
// SUBJECT
|
|
|
|
hr = AddCNAndEncode(
|
|
pSetupInfo->pwszCACommonName,
|
|
pSetupInfo->pwszDNSuffix,
|
|
&pbSubjectEncoded,
|
|
&cbSubjectEncoded);
|
|
_JumpIfError(hr, error, "AddCNAndEncodeCertStrToName");
|
|
|
|
// ISSUER
|
|
|
|
hr = AddCNAndEncode(
|
|
pSetupInfo->pwszCACommonName,
|
|
pSetupInfo->pwszDNSuffix,
|
|
&pbIssuerEncoded,
|
|
&cbIssuerEncoded);
|
|
_JumpIfError(hr, error, "AddCNAndEncodeCertStrToName");
|
|
|
|
if (!myCryptExportPublicKeyInfo(
|
|
hProv,
|
|
AT_SIGNATURE,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pPubKey,
|
|
&cbPubKey))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myCryptExportPublicKeyInfo");
|
|
}
|
|
|
|
// get cert type
|
|
hr = CAFindCertTypeByName(
|
|
pwszCAType,
|
|
NULL,
|
|
CT_FIND_LOCAL_SYSTEM |
|
|
CT_ENUM_MACHINE_TYPES |
|
|
CT_ENUM_USER_TYPES,
|
|
&hCertType);
|
|
if (HRESULT_FROM_WIN32(ERROR_NOT_FOUND) == hr)
|
|
{
|
|
hr = CAFindCertTypeByName(
|
|
pwszCAType,
|
|
NULL,
|
|
CT_FIND_LOCAL_SYSTEM |
|
|
CT_ENUM_MACHINE_TYPES |
|
|
CT_ENUM_USER_TYPES |
|
|
CT_FIND_BY_OID,
|
|
&hCertType);
|
|
}
|
|
if (S_OK == hr)
|
|
{
|
|
// get cert type standard extensions
|
|
|
|
hr = CAGetCertTypeExtensions(hCertType, &pStdExts);
|
|
_JumpIfErrorStr(hr, error, "CAGetCertTypeExtensions", pwszCAType);
|
|
|
|
cExtension = pStdExts->cExtension;
|
|
}
|
|
else
|
|
{
|
|
cExtension = 0;
|
|
DBGERRORPRINTLINE("CAFindCertTypeByName", hr);
|
|
}
|
|
|
|
if (NULL == pStdExts)
|
|
{
|
|
// standard extensions not available from CAGetCertTypeExtensions
|
|
if (!CreateKeyUsageExtension(
|
|
myCASIGN_KEY_USAGE,
|
|
&extKeyUsage.Value.pbData,
|
|
&extKeyUsage.Value.cbData,
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CreateKeyUsageExtension");
|
|
}
|
|
++cExtension;
|
|
}
|
|
|
|
hr = myInfGetBasicConstraints2CAExtensionOrDefault(hInf, &extBasicConstraints);
|
|
csiLogInfError(hInf, hr);
|
|
_JumpIfError(hr, error, "myInfGetBasicConstraints2CAExtensionOrDefault");
|
|
++cExtension;
|
|
|
|
// Subject Key Identifier extension:
|
|
|
|
hr = myCreateSubjectKeyIdentifierExtension(
|
|
pPubKey,
|
|
&extSKI.Value.pbData,
|
|
&extSKI.Value.cbData);
|
|
_JumpIfError(hr, error, "myCreateSubjectKeyIdentifierExtension");
|
|
|
|
++cExtension;
|
|
|
|
hr = CreateRevocationExtension(
|
|
hInf,
|
|
pSetupInfo->pwszSanitizedName,
|
|
0, // iCert
|
|
0, // iCRL
|
|
pSetupInfo->fUseDS,
|
|
pSetupInfo->dwRevocationFlags,
|
|
&extCDP.fCritical,
|
|
&extCDP.Value.pbData,
|
|
&extCDP.Value.cbData);
|
|
_PrintIfError(hr, "CreateRevocationExtension");
|
|
CSASSERT((NULL == extCDP.Value.pbData) ^ (S_OK == hr));
|
|
if (S_OK == hr)
|
|
{
|
|
++cExtension;
|
|
}
|
|
|
|
hr = myInfGetCrossCertDistributionPointsExtension(hInf, &extCCDP);
|
|
csiLogInfError(hInf, hr);
|
|
_PrintIfError(hr, "myInfGetCrossCertDistributionPointsExtension");
|
|
CSASSERT((NULL == extCCDP.Value.pbData) ^ (S_OK == hr));
|
|
if (S_OK == hr)
|
|
{
|
|
++cExtension;
|
|
}
|
|
|
|
// Build the CA Version extension
|
|
|
|
if (!myEncodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_INTEGER,
|
|
&pSetupInfo->dwCertNameId,
|
|
0,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&extVersion.Value.pbData,
|
|
&extVersion.Value.cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeObject");
|
|
}
|
|
++cExtension;
|
|
|
|
hr = myInfGetPolicyStatementExtension(hInf, &extPolicy);
|
|
csiLogInfError(hInf, hr);
|
|
_PrintIfError(hr, "myInfCreatePolicyStatementExtension");
|
|
CSASSERT((NULL == extPolicy.Value.pbData) ^ (S_OK == hr));
|
|
if (S_OK == hr)
|
|
{
|
|
++cExtension;
|
|
}
|
|
|
|
hr = CreateAuthorityInformationAccessExtension(
|
|
hInf,
|
|
pSetupInfo->pwszSanitizedName,
|
|
0, // iCert
|
|
0, // iCRL
|
|
pSetupInfo->fUseDS,
|
|
&extAIA.fCritical,
|
|
&extAIA.Value.pbData,
|
|
&extAIA.Value.cbData);
|
|
_PrintIfError(hr, "CreateAuthorityInformationAccessExtension");
|
|
CSASSERT((NULL == extAIA.Value.pbData) ^ (S_OK == hr));
|
|
if (S_OK == hr)
|
|
{
|
|
++cExtension;
|
|
}
|
|
|
|
hr = myInfGetEnhancedKeyUsageExtension(hInf, &extEKU);
|
|
csiLogInfError(hInf, hr);
|
|
_PrintIfError(hr, "myInfGetEnhancedKeyUsageExtension");
|
|
CSASSERT((NULL == extEKU.Value.pbData) ^ (S_OK == hr));
|
|
if (S_OK == hr)
|
|
{
|
|
++cExtension;
|
|
}
|
|
|
|
#ifdef USE_NETSCAPE_TYPE_EXTENSION
|
|
// Netscape Cert Type extension:
|
|
if (!CreateNetscapeTypeExtension(
|
|
&extNetscape.Value.pbData,
|
|
&extNetscape.Value.cbData))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CreateNetscapeTypeExtension");
|
|
}
|
|
++cExtension;
|
|
#endif
|
|
|
|
hr = myInfGetExtensions(hInf, &cExtInf, &rgExtInf);
|
|
csiLogInfError(hInf, hr);
|
|
_PrintIfError(hr, "myInfGetExtensions");
|
|
|
|
// put all extensions together
|
|
|
|
pAllExts = (CERT_EXTENSION *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cExtension * sizeof(CERT_EXTENSION));
|
|
if (NULL == pAllExts)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
i = 0;
|
|
if (NULL != pStdExts)
|
|
{
|
|
for (j = 0 ; j < pStdExts->cExtension; j++)
|
|
{
|
|
if (0 == strcmp(szOID_BASIC_CONSTRAINTS2, pStdExts->rgExtension[j].pszObjId))
|
|
{
|
|
continue;
|
|
}
|
|
pAllExts[i].pszObjId = pStdExts->rgExtension[j].pszObjId;
|
|
pAllExts[i].fCritical = pStdExts->rgExtension[j].fCritical;
|
|
pAllExts[i].Value = pStdExts->rgExtension[j].Value;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
FillExtension(pAllExts, &i, &extKeyUsage);
|
|
FillExtension(pAllExts, &i, &extBasicConstraints);
|
|
FillExtension(pAllExts, &i, &extAKI);
|
|
FillExtension(pAllExts, &i, &extSKI);
|
|
FillExtension(pAllExts, &i, &extCDP);
|
|
FillExtension(pAllExts, &i, &extCCDP);
|
|
FillExtension(pAllExts, &i, &extVersion);
|
|
FillExtension(pAllExts, &i, &extPolicy);
|
|
FillExtension(pAllExts, &i, &extAIA);
|
|
FillExtension(pAllExts, &i, &extEKU);
|
|
|
|
#ifdef USE_NETSCAPE_TYPE_EXTENSION
|
|
FillExtension(pAllExts, &i, &extKeyNetscape);
|
|
#endif
|
|
CSASSERT(i <= cExtension);
|
|
|
|
// CERT
|
|
ZeroMemory(&Cert, sizeof(Cert));
|
|
Cert.dwVersion = CERT_V3;
|
|
myGenerateGuidSerialNumber(&guidSerialNumber);
|
|
|
|
Cert.SerialNumber.pbData = (BYTE *) &guidSerialNumber;
|
|
Cert.SerialNumber.cbData = sizeof(guidSerialNumber);
|
|
Cert.SignatureAlgorithm.pszObjId = pSetupInfo->pszAlgId;
|
|
Cert.Issuer.pbData = pbIssuerEncoded;
|
|
Cert.Issuer.cbData = cbIssuerEncoded;
|
|
|
|
GetSystemTimeAsFileTime(&Cert.NotBefore);
|
|
myMakeExprDateTime(
|
|
&Cert.NotBefore,
|
|
-CCLOCKSKEWMINUTESDEFAULT,
|
|
ENUM_PERIOD_MINUTES);
|
|
if (0 < CompareFileTime(&Cert.NotBefore, &pSetupInfo->NotBefore))
|
|
{
|
|
Cert.NotBefore = pSetupInfo->NotBefore;
|
|
}
|
|
Cert.NotAfter = pSetupInfo->NotAfter;
|
|
|
|
Cert.Subject.pbData = pbSubjectEncoded;
|
|
Cert.Subject.cbData = cbSubjectEncoded;
|
|
Cert.SubjectPublicKeyInfo = *pPubKey; // Structure assignment
|
|
|
|
Cert.cExtension = i;
|
|
Cert.rgExtension = pAllExts;
|
|
if (0 != cExtInf)
|
|
{
|
|
hr = myMergeExtensions(
|
|
Cert.cExtension,
|
|
Cert.rgExtension,
|
|
cExtInf,
|
|
rgExtInf,
|
|
&cExtMerged,
|
|
&rgExtMerged);
|
|
_JumpIfError(hr, error, "myMergeExtensions");
|
|
|
|
Cert.cExtension = cExtMerged;
|
|
Cert.rgExtension = rgExtMerged;
|
|
}
|
|
if (0 == Cert.cExtension)
|
|
{
|
|
Cert.dwVersion = CERT_V1;
|
|
}
|
|
|
|
hr = EncodeCertAndSign(
|
|
hProv,
|
|
&Cert,
|
|
pSetupInfo->pszAlgId,
|
|
ppbEncoded,
|
|
pcbEncoded,
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd);
|
|
_JumpIfError(hr, error, "EncodeCertAndSign");
|
|
|
|
error:
|
|
if (INVALID_HANDLE_VALUE != hInf)
|
|
{
|
|
myInfCloseFile(hInf);
|
|
}
|
|
if (NULL != rgExtMerged)
|
|
{
|
|
LocalFree(rgExtMerged);
|
|
}
|
|
myInfFreeExtensions(cExtInf, rgExtInf);
|
|
if (NULL != extKeyUsage.Value.pbData)
|
|
{
|
|
LocalFree(extKeyUsage.Value.pbData);
|
|
}
|
|
if (NULL != extBasicConstraints.Value.pbData)
|
|
{
|
|
LocalFree(extBasicConstraints.Value.pbData);
|
|
}
|
|
if (NULL != extAKI.Value.pbData)
|
|
{
|
|
LocalFree(extAKI.Value.pbData);
|
|
}
|
|
if (NULL != extSKI.Value.pbData)
|
|
{
|
|
LocalFree(extSKI.Value.pbData);
|
|
}
|
|
if (NULL != extCDP.Value.pbData)
|
|
{
|
|
LocalFree(extCDP.Value.pbData);
|
|
}
|
|
if (NULL != extCCDP.Value.pbData)
|
|
{
|
|
LocalFree(extCCDP.Value.pbData);
|
|
}
|
|
if (NULL != extVersion.Value.pbData)
|
|
{
|
|
LocalFree(extVersion.Value.pbData);
|
|
}
|
|
if (NULL != extPolicy.Value.pbData)
|
|
{
|
|
LocalFree(extPolicy.Value.pbData);
|
|
}
|
|
if (NULL != extAIA.Value.pbData)
|
|
{
|
|
LocalFree(extAIA.Value.pbData);
|
|
}
|
|
if (NULL != extEKU.Value.pbData)
|
|
{
|
|
LocalFree(extEKU.Value.pbData);
|
|
}
|
|
#ifdef USE_NETSCAPE_TYPE_EXTENSION
|
|
if (NULL != extKeyNetscape.Value.pbData)
|
|
{
|
|
LocalFree(extKeyNetscape.Value.pbData);
|
|
}
|
|
#endif
|
|
if (NULL != hCertType)
|
|
{
|
|
if (NULL != pStdExts)
|
|
{
|
|
CAFreeCertTypeExtensions(hCertType, pStdExts);
|
|
}
|
|
CACloseCertType(hCertType);
|
|
}
|
|
if (NULL != pAllExts)
|
|
{
|
|
LocalFree(pAllExts);
|
|
}
|
|
if (NULL != pbSubjectEncoded)
|
|
{
|
|
LocalFree(pbSubjectEncoded);
|
|
}
|
|
if (NULL != pbIssuerEncoded)
|
|
{
|
|
LocalFree(pbIssuerEncoded);
|
|
}
|
|
if (NULL != pPubKey)
|
|
{
|
|
LocalFree(pPubKey);
|
|
}
|
|
CSILOG(hr, IDS_ILOG_BUILDCERT, NULL, NULL, NULL);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiGetCRLPublicationParams(
|
|
BOOL fBaseCRL,
|
|
WCHAR **ppwszCRLPeriodString,
|
|
DWORD *pdwCRLPeriodCount)
|
|
{
|
|
HRESULT hr;
|
|
HINF hInf = INVALID_HANDLE_VALUE;
|
|
DWORD ErrorLine;
|
|
static WCHAR const * const s_apwszKeys[] =
|
|
{
|
|
wszINFKEY_CRLPERIODSTRING,
|
|
wszINFKEY_CRLDELTAPERIODSTRING,
|
|
wszINFKEY_CRLPERIODCOUNT,
|
|
wszINFKEY_CRLDELTAPERIODCOUNT,
|
|
NULL
|
|
};
|
|
|
|
hr = myInfOpenFile(NULL, &hInf, &ErrorLine);
|
|
_JumpIfError2(
|
|
hr,
|
|
error,
|
|
"myInfOpenFile",
|
|
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
|
|
|
|
|
|
if (INVALID_HANDLE_VALUE != hInf)
|
|
{
|
|
HRESULT hr2;
|
|
|
|
hr = myInfGetCRLPublicationParams(
|
|
hInf,
|
|
fBaseCRL? wszINFKEY_CRLPERIODSTRING : wszINFKEY_CRLDELTAPERIODSTRING,
|
|
fBaseCRL? wszINFKEY_CRLPERIODCOUNT : wszINFKEY_CRLDELTAPERIODCOUNT,
|
|
ppwszCRLPeriodString,
|
|
pdwCRLPeriodCount);
|
|
|
|
// log any error, but befre returning the eorror, also log any
|
|
// unexpected Keys in the same INF file section, so the log will
|
|
// describe any Key name typos.
|
|
|
|
csiLogInfError(hInf, hr);
|
|
_PrintIfErrorStr(
|
|
hr,
|
|
"myInfGetCRLPublicationParams",
|
|
fBaseCRL? L"Base" : L"Delta");
|
|
|
|
hr2 = myInfGetKeyList(
|
|
hInf,
|
|
wszINFSECTION_CERTSERVER,
|
|
NULL, // pwszKey
|
|
s_apwszKeys,
|
|
NULL, // pfCritical
|
|
NULL); // ppwszzTemplateList
|
|
csiLogInfError(hInf, hr2);
|
|
_PrintIfErrorStr(hr2, "myInfGetKeyList", wszINFSECTION_CERTSERVER);
|
|
|
|
_JumpIfError(hr, error, "myInfGetCRLPublicationParams");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (INVALID_HANDLE_VALUE != hInf)
|
|
{
|
|
myInfCloseFile(hInf);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiBuildFileName(
|
|
IN WCHAR const *pwszDirPath,
|
|
IN WCHAR const *pwszSanitizedName,
|
|
IN WCHAR const *pwszExt,
|
|
IN DWORD iCert,
|
|
OUT WCHAR **ppwszOut,
|
|
HINSTANCE hInstance,
|
|
BOOL fUnattended,
|
|
IN HWND hwnd)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cwc;
|
|
WCHAR *pwszServerName = NULL;
|
|
WCHAR wszIndex[cwcFILENAMESUFFIXMAX]; // L"(%u)"
|
|
|
|
*ppwszOut = NULL;
|
|
wszIndex[0] = L'\0';
|
|
if (0 != iCert)
|
|
{
|
|
wsprintf(wszIndex, L"(%u)", iCert);
|
|
}
|
|
|
|
hr = myGetMachineDnsName(&pwszServerName);
|
|
if (S_OK != hr)
|
|
{
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_GETCOMPUTERNAME,
|
|
hr,
|
|
NULL);
|
|
_JumpError(hr, error, "myGetMachineDnsName");
|
|
}
|
|
|
|
cwc = wcslen(pwszDirPath) +
|
|
WSZARRAYSIZE(g_szSlash) +
|
|
wcslen(pwszServerName) +
|
|
WSZARRAYSIZE(L"_") +
|
|
wcslen(pwszSanitizedName) +
|
|
wcslen(wszIndex) +
|
|
wcslen(pwszExt) +
|
|
1; // NULL term
|
|
|
|
*ppwszOut = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR));
|
|
if (NULL == *ppwszOut)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
wcscpy(*ppwszOut, pwszDirPath);
|
|
wcscat(*ppwszOut, g_szSlash);
|
|
wcscat(*ppwszOut, pwszServerName);
|
|
wcscat(*ppwszOut, L"_");
|
|
wcscat(*ppwszOut, pwszSanitizedName);
|
|
wcscat(*ppwszOut, wszIndex);
|
|
wcscat(*ppwszOut, pwszExt);
|
|
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszServerName)
|
|
{
|
|
LocalFree(pwszServerName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiBuildCACertFileName(
|
|
IN HINSTANCE hInstance,
|
|
IN HWND hwnd,
|
|
IN BOOL fUnattended,
|
|
OPTIONAL IN WCHAR const *pwszSharedFolder,
|
|
IN WCHAR const *pwszSanitizedName,
|
|
IN WCHAR const *pwszExt,
|
|
IN DWORD iCert,
|
|
OUT WCHAR **ppwszCACertFile)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszCACertFile = NULL;
|
|
WCHAR const *pwszDir = pwszSharedFolder;
|
|
WCHAR *pwszDirAlloc = NULL;
|
|
|
|
CSASSERT(NULL != ppwszCACertFile);
|
|
*ppwszCACertFile = NULL;
|
|
|
|
if (NULL == pwszDir)
|
|
{
|
|
// no shared folder, go system drive
|
|
hr = myGetEnvString(&pwszDirAlloc, L"SystemDrive");
|
|
_JumpIfError(hr, error, "myGetEnvString");
|
|
|
|
pwszDir = pwszDirAlloc;
|
|
}
|
|
// build ca cert file name here
|
|
hr = csiBuildFileName(
|
|
pwszDir,
|
|
pwszSanitizedName,
|
|
pwszExt,
|
|
iCert,
|
|
&pwszCACertFile,
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd);
|
|
_JumpIfError(hr, error, "csiBuildFileName");
|
|
|
|
CSASSERT(NULL != pwszCACertFile);
|
|
|
|
*ppwszCACertFile = pwszCACertFile;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszDirAlloc)
|
|
{
|
|
LocalFree(pwszDirAlloc);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiBuildAndWriteCert(
|
|
IN HCRYPTPROV hCryptProv,
|
|
IN CASERVERSETUPINFO const *pServer,
|
|
OPTIONAL IN WCHAR const *pwszFile,
|
|
IN WCHAR const *pwszEnrollFile,
|
|
OPTIONAL IN CERT_CONTEXT const *pCertContextFromStore,
|
|
OPTIONAL OUT CERT_CONTEXT const **ppCertContextOut,
|
|
IN WCHAR const *pwszCAType,
|
|
IN HINSTANCE hInstance,
|
|
IN BOOL fUnattended,
|
|
IN HWND hwnd)
|
|
{
|
|
BYTE *pbEncoded = NULL;
|
|
DWORD cbEncoded;
|
|
CERT_CONTEXT const *pccCA = NULL;
|
|
HRESULT hr;
|
|
|
|
if (NULL != ppCertContextOut)
|
|
{
|
|
*ppCertContextOut = NULL;
|
|
}
|
|
if (NULL == pCertContextFromStore)
|
|
{
|
|
// create cert
|
|
hr = EncodeCACert(
|
|
pServer,
|
|
hCryptProv,
|
|
pwszCAType,
|
|
&pbEncoded,
|
|
&cbEncoded,
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd);
|
|
_JumpIfError(hr, error, "EncodeCACert");
|
|
|
|
pccCA = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
pbEncoded,
|
|
cbEncoded);
|
|
if (NULL == pccCA)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pccCA = CertDuplicateCertificateContext(pCertContextFromStore);
|
|
if (NULL == pccCA)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertDuplicateCertificateContext");
|
|
}
|
|
}
|
|
|
|
if (NULL != pwszFile && !csiWriteDERToFile(
|
|
pwszFile,
|
|
pccCA->pbCertEncoded,
|
|
pccCA->cbCertEncoded,
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "csiWriteDERToFile");
|
|
}
|
|
|
|
if (!csiWriteDERToFile(
|
|
pwszEnrollFile,
|
|
pccCA->pbCertEncoded,
|
|
pccCA->cbCertEncoded,
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "csiWriteDERToFile(enroll)");
|
|
}
|
|
|
|
if (NULL != ppCertContextOut)
|
|
{
|
|
*ppCertContextOut = pccCA;
|
|
pccCA = NULL;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pccCA)
|
|
{
|
|
if (!CertFreeCertificateContext(pccCA))
|
|
{
|
|
HRESULT hr2;
|
|
|
|
hr2 = myHLastError();
|
|
_PrintError(hr2, "CertFreeCertificateContext");
|
|
CSASSERT(S_OK == hr2);
|
|
}
|
|
}
|
|
|
|
if (NULL != pbEncoded)
|
|
{
|
|
LocalFree(pbEncoded);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
IsCACert(
|
|
IN HINSTANCE hInstance,
|
|
IN BOOL fUnattended,
|
|
IN HWND hwnd,
|
|
IN CERT_CONTEXT const *pCert,
|
|
IN ENUM_CATYPES CAType)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fCA;
|
|
CERT_EXTENSION *pExt;
|
|
DWORD cb;
|
|
CERT_BASIC_CONSTRAINTS2_INFO Constraints;
|
|
|
|
fCA = FALSE;
|
|
hr = S_OK;
|
|
pExt = CertFindExtension(
|
|
szOID_BASIC_CONSTRAINTS2,
|
|
pCert->pCertInfo->cExtension,
|
|
pCert->pCertInfo->rgExtension);
|
|
if (NULL == pExt)
|
|
{
|
|
if (IsRootCA(CAType) && CERT_V1 == pCert->pCertInfo->dwVersion)
|
|
{
|
|
_PrintError(hr, "V1 root cert");
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_PrintError(hr, "No Basic Constraints Extension");
|
|
}
|
|
else
|
|
{
|
|
cb = sizeof(Constraints);
|
|
if (!CryptDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_BASIC_CONSTRAINTS2,
|
|
pExt->Value.pbData,
|
|
pExt->Value.cbData,
|
|
0,
|
|
&Constraints,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_PrintError(hr, "CryptDecodeObject");
|
|
}
|
|
else
|
|
{
|
|
fCA = Constraints.fCA;
|
|
if (!fCA)
|
|
{
|
|
hr = CERTSRV_E_INVALID_CA_CERTIFICATE;
|
|
_PrintError(hr, "fCA not set");
|
|
}
|
|
}
|
|
}
|
|
if (!fCA)
|
|
{
|
|
CertMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_NOTCACERT,
|
|
S_OK,
|
|
MB_OK | MB_ICONERROR | CMB_NOERRFROMSYS,
|
|
NULL);
|
|
_JumpError(hr, error, "not a CA cert");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ExtractCACertFromPKCS7(
|
|
IN WCHAR const *pwszCommonName,
|
|
IN BYTE const *pbPKCS7,
|
|
IN DWORD cbPKCS7,
|
|
OPTIONAL OUT CERT_CONTEXT const **ppccCA)
|
|
{
|
|
HRESULT hr;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
HCERTSTORE hChainStore = NULL;
|
|
CRYPT_DATA_BLOB chainBlob;
|
|
CERT_RDN_ATTR rdnAttr = { szOID_COMMON_NAME, CERT_RDN_ANY_TYPE, };
|
|
CERT_RDN rdn = { 1, &rdnAttr };
|
|
CERT_CHAIN_PARA ChainPara;
|
|
CERT_CHAIN_CONTEXT const *pChainContext = NULL;
|
|
CERT_CHAIN_CONTEXT const *pLongestChainContext = NULL;
|
|
|
|
*ppccCA = NULL;
|
|
if (NULL == pbPKCS7 || 0 == cbPKCS7)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "Invalid input parameters");
|
|
}
|
|
|
|
chainBlob.pbData = const_cast<BYTE *>(pbPKCS7);
|
|
chainBlob.cbData = cbPKCS7;
|
|
hChainStore = CertOpenStore(
|
|
CERT_STORE_PROV_PKCS7,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
NULL, // hProv
|
|
0,
|
|
(const void*) &chainBlob);
|
|
if (NULL == hChainStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
|
|
rdnAttr.Value.pbData = (BYTE *) pwszCommonName;
|
|
rdnAttr.Value.cbData = 0;
|
|
|
|
// Find the longest chain in the passed PKCS7 with a leaf CA cert that
|
|
// matches the passed common name
|
|
|
|
for (;;)
|
|
{
|
|
pCert = CertFindCertificateInStore(
|
|
hChainStore,
|
|
X509_ASN_ENCODING,
|
|
CERT_UNICODE_IS_RDN_ATTRS_FLAG |
|
|
CERT_CASE_INSENSITIVE_IS_RDN_ATTRS_FLAG,
|
|
CERT_FIND_SUBJECT_ATTR,
|
|
&rdn,
|
|
pCert);
|
|
if (NULL == pCert)
|
|
{
|
|
if (NULL == pLongestChainContext)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "can't find matched cert in chain");
|
|
}
|
|
break; // most common case, done here
|
|
}
|
|
|
|
ZeroMemory(&ChainPara, sizeof(ChainPara));
|
|
ChainPara.cbSize = sizeof(CERT_CHAIN_PARA);
|
|
ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
|
|
//ChainPara.RequestedUsage.Usage.cUsageIdentifier = 0;
|
|
//ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = NULL;
|
|
|
|
if (!CertGetCertificateChain(
|
|
HCCE_LOCAL_MACHINE,
|
|
pCert,
|
|
NULL,
|
|
hChainStore,
|
|
&ChainPara,
|
|
0,
|
|
NULL,
|
|
&pChainContext))
|
|
{
|
|
// couldn't get the chain
|
|
|
|
if (NULL == pLongestChainContext)
|
|
{
|
|
// fail to find a chain
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateChain");
|
|
}
|
|
break; // done with it
|
|
}
|
|
|
|
// we have assumed each chain context contains
|
|
// only one simple chain, ie. pChainContext->cChain = 1
|
|
CSASSERT(1 == pChainContext->cChain);
|
|
|
|
if (NULL == pLongestChainContext ||
|
|
pChainContext->rgpChain[0]->cElement >
|
|
pLongestChainContext->rgpChain[0]->cElement)
|
|
{
|
|
if (NULL != pLongestChainContext)
|
|
{
|
|
CertFreeCertificateChain(pLongestChainContext);
|
|
}
|
|
|
|
// save pointer to this chain
|
|
|
|
pLongestChainContext = pChainContext;
|
|
}
|
|
else
|
|
{
|
|
CertFreeCertificateChain(pChainContext);
|
|
}
|
|
}
|
|
CSASSERT(NULL == pCert);
|
|
if (NULL != pLongestChainContext &&
|
|
0 < pLongestChainContext->rgpChain[0]->cElement)
|
|
{
|
|
*ppccCA = CertDuplicateCertificateContext(
|
|
pLongestChainContext->rgpChain[0]->rgpElement[0]->pCertContext);
|
|
if (NULL == *ppccCA)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertDuplicateCertificateContext");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pCert)
|
|
{
|
|
CertFreeCertificateContext(pCert);
|
|
}
|
|
if (NULL != pLongestChainContext)
|
|
{
|
|
CertFreeCertificateChain(pLongestChainContext);
|
|
}
|
|
if (hChainStore)
|
|
{
|
|
CertCloseStore(hChainStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
CSILOG(hr, IDS_ILOG_SAVECHAINANDKEYS, pwszCommonName, NULL, NULL);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
#define ENTERPRISECATEMPLATELIST \
|
|
wszCERTTYPE_ADMIN, \
|
|
wszCERTTYPE_SUBORDINATE_CA, \
|
|
wszCERTTYPE_USER, \
|
|
wszCERTTYPE_MACHINE, \
|
|
wszCERTTYPE_WEBSERVER, \
|
|
wszCERTTYPE_DC, \
|
|
wszCERTTYPE_EFS, \
|
|
wszCERTTYPE_EFS_RECOVERY
|
|
|
|
WCHAR *s_apwszCertTypeServer[] =
|
|
{
|
|
ENTERPRISECATEMPLATELIST,
|
|
NULL
|
|
};
|
|
|
|
WCHAR *s_apwszCertTypeAdvancedServer[] =
|
|
{
|
|
ENTERPRISECATEMPLATELIST,
|
|
wszCERTTYPE_DC_AUTH,
|
|
wszCERTTYPE_DS_EMAIL_REPLICATION,
|
|
NULL
|
|
};
|
|
|
|
WCHAR *s_apwszCertTypeEmpty[] =
|
|
{
|
|
L" ",
|
|
NULL
|
|
};
|
|
|
|
|
|
HRESULT
|
|
GetValidCRLIndexes(
|
|
IN LPCWSTR pwszSanitizedCAName,
|
|
OUT DWORD **ppdwCRLIndexes,
|
|
OUT DWORD *pnCRLIndexes)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwHashCount;
|
|
HCERTSTORE hMyStore = NULL;
|
|
CERT_CONTEXT const *pccCert = NULL;
|
|
DWORD NameId;
|
|
DWORD nCRLIndexes;
|
|
DWORD *pdwCRLIndexes = NULL;
|
|
|
|
*ppdwCRLIndexes = NULL;
|
|
*pnCRLIndexes = 0;
|
|
|
|
hr = myGetCARegHashCount(pwszSanitizedCAName, CSRH_CASIGCERT, &dwHashCount);
|
|
_JumpIfError(hr, error, "myGetCARegHashCount");
|
|
|
|
pdwCRLIndexes = (DWORD *) LocalAlloc(LMEM_FIXED, dwHashCount*sizeof(DWORD));
|
|
_JumpIfAllocFailed(pdwCRLIndexes, error);
|
|
|
|
hMyStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE |
|
|
CERT_STORE_READONLY_FLAG |
|
|
CERT_STORE_ENUM_ARCHIVED_FLAG,
|
|
wszMY_CERTSTORE);
|
|
if (NULL == hMyStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
|
|
for(DWORD dwCount=0; dwCount<dwHashCount; dwCount++)
|
|
{
|
|
hr = myFindCACertByHashIndex(
|
|
hMyStore,
|
|
pwszSanitizedCAName,
|
|
CSRH_CASIGCERT,
|
|
dwCount,
|
|
&NameId,
|
|
&pccCert);
|
|
if (S_FALSE == hr)
|
|
{
|
|
continue;
|
|
}
|
|
_JumpIfError(hr, error, "myFindCACertByHashIndex");
|
|
|
|
if (MAXDWORD==NameId)
|
|
{
|
|
pdwCRLIndexes[dwCount] = dwCount;
|
|
}
|
|
else
|
|
{
|
|
pdwCRLIndexes[dwCount] = CANAMEIDTOIKEY(NameId);
|
|
}
|
|
|
|
CertFreeCertificateContext(pccCert);
|
|
pccCert = NULL;
|
|
}
|
|
|
|
// The index list looks like this: 0 1 2 2 3 4 5 5 5 6. Compact it
|
|
// in place, eliminating duplicates.
|
|
nCRLIndexes = 0;
|
|
for(DWORD dwCount=0; dwCount<dwHashCount;dwCount++)
|
|
{
|
|
if(dwCount>0 &&
|
|
pdwCRLIndexes[dwCount] == pdwCRLIndexes[dwCount-1])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pdwCRLIndexes[nCRLIndexes] = pdwCRLIndexes[dwCount];
|
|
|
|
nCRLIndexes++;
|
|
}
|
|
|
|
*pnCRLIndexes = nCRLIndexes;
|
|
*ppdwCRLIndexes = pdwCRLIndexes;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if(S_OK != hr &&
|
|
NULL != pdwCRLIndexes)
|
|
{
|
|
LocalFree(pdwCRLIndexes);
|
|
}
|
|
if(NULL != pccCert)
|
|
{
|
|
CertFreeCertificateContext(pccCert);
|
|
}
|
|
if (NULL != hMyStore)
|
|
{
|
|
CertCloseStore(hMyStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CreateCDPAndAIAAndKRAEntry(
|
|
IN WCHAR const *pwszSanitizedCAName,
|
|
IN WCHAR const *pwszServerName,
|
|
IN DWORD iCert,
|
|
IN DWORD iCRL,
|
|
IN BYTE const *pbCert,
|
|
IN DWORD cbCert,
|
|
IN PSECURITY_DESCRIPTOR pSD,
|
|
IN PSECURITY_DESCRIPTOR pContainerSD)
|
|
{
|
|
HRESULT hr;
|
|
LDAP *pld = NULL;
|
|
BSTR strConfigDN = NULL;
|
|
BSTR strDomainDN = NULL;
|
|
WCHAR *pwszCDPDN = NULL;
|
|
WCHAR *pwszAIADN;
|
|
WCHAR *pwszKRADN;
|
|
WCHAR const *apwszIn[2];
|
|
WCHAR *apwszOut[2];
|
|
DWORD i;
|
|
DWORD dwDisp;
|
|
WCHAR *pwszError = NULL;
|
|
DWORD *pdwCRLIndexes = NULL;
|
|
DWORD nCRLIndexes;
|
|
|
|
ZeroMemory(apwszOut, sizeof(apwszOut));
|
|
|
|
hr = myLdapOpen(
|
|
NULL, // pwszDomainName
|
|
RLBF_REQUIRE_SECURE_LDAP, // dwFlags
|
|
&pld,
|
|
&strDomainDN,
|
|
&strConfigDN);
|
|
_JumpIfError(hr, error, "myLdapOpen");
|
|
|
|
DBGPRINT((DBG_SS_CERTLIBI, "DomainDN='%ws'\n", strDomainDN));
|
|
DBGPRINT((DBG_SS_CERTLIBI, "ConfigDN='%ws'\n", strConfigDN));
|
|
|
|
//+=====================================================================
|
|
// Create the CDP container and objects:
|
|
|
|
hr = GetValidCRLIndexes(
|
|
pwszSanitizedCAName,
|
|
&pdwCRLIndexes,
|
|
&nCRLIndexes);
|
|
_JumpIfError(hr, error, "GetValidCRLIndexes");
|
|
|
|
apwszIn[0] = g_wszCDPDNTemplate;
|
|
|
|
for(DWORD dwCount=0; dwCount<nCRLIndexes; dwCount++)
|
|
{
|
|
hr = myFormatCertsrvStringArray(
|
|
FALSE, // fURL
|
|
pwszServerName, // pwszServerName_p1_2
|
|
pwszSanitizedCAName,// pwszSanitizedName_p3_7
|
|
iCert, // iCert_p4
|
|
MAXDWORD, // iCertTarget_p4
|
|
strDomainDN, // pwszDomainDN_p5
|
|
strConfigDN, // pwszConfigDN_p6
|
|
pdwCRLIndexes[dwCount], // iCRL_p8
|
|
FALSE, // fDeltaCRL_p9
|
|
FALSE, // fDSAttrib_p10_11
|
|
1, // cStrings
|
|
(LPCWSTR *) apwszIn,// apwszStringsIn
|
|
apwszOut); // apwszStringsOut
|
|
_JumpIfError(hr, error, "myFormatCertsrvStringArray");
|
|
|
|
// attemp to create container just once
|
|
if(0==dwCount)
|
|
{
|
|
hr = myLdapCreateContainer(
|
|
pld,
|
|
apwszOut[0],
|
|
TRUE,
|
|
1,
|
|
pContainerSD,
|
|
&pwszError);
|
|
_JumpIfError(hr, error, "myLdapCreateContainer");
|
|
|
|
CSASSERT(NULL == pwszError);
|
|
}
|
|
|
|
DBGPRINT((DBG_SS_CERTLIBI, "CDPDN='%ws'\n", apwszOut[0]));
|
|
|
|
hr = myLdapCreateCDPObject(pld, apwszOut[0], pSD, &dwDisp, &pwszError);
|
|
_JumpIfErrorStr(hr, error, "myLdapCreateCDPObject", apwszOut[0]);
|
|
|
|
CSASSERT(NULL == pwszError);
|
|
|
|
LocalFree(apwszOut[0]);
|
|
apwszOut[0] = NULL;
|
|
}
|
|
|
|
//+=====================================================================
|
|
// Create the KRA and AIA containers and objects
|
|
|
|
apwszIn[0] = g_wszAIADNTemplate;
|
|
apwszIn[1] = g_wszKRADNTemplate;
|
|
|
|
// Format the KRA and AIA templates into real names
|
|
|
|
hr = myFormatCertsrvStringArray(
|
|
FALSE, // fURL
|
|
pwszServerName, // pwszServerName_p1_2
|
|
pwszSanitizedCAName, // pwszSanitizedName_p3_7
|
|
iCert, // iCert_p4
|
|
MAXDWORD, // iCertTarget_p4
|
|
strDomainDN, // pwszDomainDN_p5
|
|
strConfigDN, // pwszConfigDN_p6
|
|
iCRL, // iCRL_p8
|
|
FALSE, // fDeltaCRL_p9
|
|
FALSE, // fDSAttrib_p10_11
|
|
ARRAYSIZE(apwszIn), // cStrings
|
|
(LPCWSTR *) apwszIn, // apwszStringsIn
|
|
apwszOut); // apwszStringsOut
|
|
_JumpIfError(hr, error, "myFormatCertsrvStringArray");
|
|
|
|
pwszAIADN = apwszOut[0];
|
|
pwszKRADN = apwszOut[1];
|
|
|
|
|
|
DBGPRINT((DBG_SS_CERTLIBI, "AIADN='%ws'\n", pwszAIADN));
|
|
DBGPRINT((DBG_SS_CERTLIBI, "KRADN='%ws'\n", pwszKRADN));
|
|
|
|
//+=====================================================================
|
|
// Create the container and AIA object:
|
|
|
|
hr = myLdapCreateContainer(
|
|
pld,
|
|
pwszAIADN,
|
|
TRUE,
|
|
0,
|
|
pContainerSD,
|
|
&pwszError);
|
|
_JumpIfError(hr, error, "myLdapCreateContainer");
|
|
|
|
CSASSERT(NULL == pwszError);
|
|
|
|
hr = myLdapCreateCAObject(
|
|
pld,
|
|
pwszAIADN,
|
|
pbCert,
|
|
cbCert,
|
|
pSD,
|
|
&dwDisp,
|
|
&pwszError);
|
|
_JumpIfErrorStr(hr, error, "myLdapCreateCAObject", pwszAIADN);
|
|
|
|
hr = myLdapFilterCertificates(
|
|
pld,
|
|
pwszAIADN,
|
|
wszDSCACERTATTRIBUTE,
|
|
&dwDisp,
|
|
&pwszError);
|
|
_JumpIfErrorStr(hr, error, "myLdapFilterCertificates", pwszAIADN);
|
|
|
|
|
|
CSASSERT(NULL == pwszError);
|
|
|
|
|
|
//+=====================================================================
|
|
// Create the KRA container and object:
|
|
|
|
hr = myLdapCreateContainer(
|
|
pld,
|
|
pwszKRADN,
|
|
TRUE,
|
|
0,
|
|
pContainerSD,
|
|
&pwszError);
|
|
_JumpIfError(hr, error, "myLdapCreateContainer");
|
|
|
|
CSASSERT(NULL == pwszError);
|
|
|
|
hr = myLdapCreateUserObject(
|
|
pld,
|
|
pwszKRADN,
|
|
NULL,
|
|
0,
|
|
pSD,
|
|
LPC_KRAOBJECT,
|
|
&dwDisp,
|
|
&pwszError);
|
|
//_JumpIfErrorStr(hr, error, "myLdapCreateUserObject", pwszKRADN);
|
|
_PrintIfErrorStr(hr, "myLdapCreateUserObject", pwszKRADN);
|
|
CSASSERT(S_OK == hr || NULL != pwszError);
|
|
hr = S_OK;
|
|
|
|
error:
|
|
CSILOG(hr, IDS_ILOG_CREATECDP, pwszCDPDN, pwszError, NULL);
|
|
if (NULL != pdwCRLIndexes)
|
|
{
|
|
LocalFree(pdwCRLIndexes);
|
|
}
|
|
if (NULL != pwszError)
|
|
{
|
|
LocalFree(pwszError);
|
|
}
|
|
for (i = 0; i < ARRAYSIZE(apwszOut); i++)
|
|
{
|
|
if (NULL != apwszOut[i])
|
|
{
|
|
LocalFree(apwszOut[i]);
|
|
}
|
|
}
|
|
myLdapClose(pld, strDomainDN, strConfigDN);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CreateEnterpriseAndRootEntry(
|
|
IN WCHAR const *pwszSanitizedDSName,
|
|
IN CERT_CONTEXT const *pccPublish,
|
|
IN ENUM_CATYPES caType,
|
|
IN PSECURITY_DESCRIPTOR pSD,
|
|
IN PSECURITY_DESCRIPTOR pContainerSD)
|
|
{
|
|
HRESULT hr;
|
|
LDAP *pld = NULL;
|
|
BSTR strConfig = NULL;
|
|
BSTR strDomainDN = NULL;
|
|
WCHAR *pwszRootDN = NULL;
|
|
WCHAR *pwszEnterpriseDN = NULL;
|
|
PSECURITY_DESCRIPTOR pNTAuthSD = NULL;
|
|
DWORD cwc;
|
|
DWORD dwDisp;
|
|
WCHAR *pwszError = NULL;
|
|
|
|
if (!IsEnterpriseCA(caType) && !IsRootCA(caType))
|
|
{
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
hr = myLdapOpen(
|
|
NULL, // pwszDomainName
|
|
RLBF_REQUIRE_SECURE_LDAP, // dwFlags
|
|
&pld,
|
|
&strDomainDN,
|
|
&strConfig);
|
|
_JumpIfError(hr, error, "myLdapOpen");
|
|
|
|
cwc = WSZARRAYSIZE(L"CN=") +
|
|
wcslen(pwszSanitizedDSName) +
|
|
WSZARRAYSIZE(s_wszRootCAs) +
|
|
wcslen(strConfig);
|
|
pwszRootDN = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR));
|
|
if (pwszRootDN == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpIfError(hr, error, "LocalAlloc");
|
|
}
|
|
wcscpy(pwszRootDN, L"CN=");
|
|
wcscat(pwszRootDN, pwszSanitizedDSName);
|
|
wcscat(pwszRootDN, s_wszRootCAs);
|
|
wcscat(pwszRootDN, strConfig);
|
|
CSASSERT(wcslen(pwszRootDN) == cwc);
|
|
|
|
cwc = wcslen(s_wszEnterpriseCAs) + wcslen(strConfig);
|
|
pwszEnterpriseDN = (WCHAR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(cwc + 1) * sizeof(WCHAR));
|
|
if (pwszEnterpriseDN == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpIfError(hr, error, "LocalAlloc");
|
|
}
|
|
wcscpy(pwszEnterpriseDN, s_wszEnterpriseCAs);
|
|
wcscat(pwszEnterpriseDN, strConfig);
|
|
CSASSERT(wcslen(pwszEnterpriseDN) == cwc);
|
|
|
|
//+=====================================================================
|
|
// Create the root trust CA container and entry (Root only):
|
|
|
|
if (IsRootCA(caType))
|
|
{
|
|
DBGPRINT((DBG_SS_CERTLIBI, "Creating Services Containers: '%ws'\n", pwszRootDN));
|
|
hr = myLdapCreateContainer(
|
|
pld,
|
|
pwszRootDN,
|
|
TRUE,
|
|
1,
|
|
pContainerSD,
|
|
&pwszError);
|
|
_JumpIfError(hr, error, "myLdapCreateContainer");
|
|
|
|
CSASSERT(NULL == pwszError);
|
|
|
|
DBGPRINT((DBG_SS_CERTLIBI, "Creating DS Root Trust: '%ws'\n", pwszRootDN));
|
|
hr = myLdapCreateCAObject(
|
|
pld,
|
|
pwszRootDN,
|
|
pccPublish->pbCertEncoded,
|
|
pccPublish->cbCertEncoded,
|
|
pSD,
|
|
&dwDisp,
|
|
&pwszError);
|
|
_JumpIfErrorStr(hr, error, "myLdapCreateCAObject", pwszRootDN);
|
|
}
|
|
|
|
//+=====================================================================
|
|
// Create the NTAuth trust entry (Enterprise only):
|
|
|
|
if (IsEnterpriseCA(caType))
|
|
{
|
|
DBGPRINT((
|
|
DBG_SS_CERTLIBI,
|
|
"Creating DS Enterprise Trust: '%ws'\n",
|
|
pwszEnterpriseDN));
|
|
|
|
hr = myGetSDFromTemplate(WSZ_DEFAULT_NTAUTH_SECURITY, NULL, &pNTAuthSD);
|
|
_JumpIfError(hr, error, "myGetSDFromTemplate");
|
|
|
|
hr = myLdapCreateCAObject(
|
|
pld,
|
|
pwszEnterpriseDN,
|
|
NULL,
|
|
0,
|
|
pNTAuthSD,
|
|
&dwDisp,
|
|
&pwszError);
|
|
_JumpIfErrorStr(hr, error, "myLdapCreateCAObject", pwszEnterpriseDN);
|
|
|
|
hr = AddCertToAttribute(
|
|
pld,
|
|
pccPublish,
|
|
pwszEnterpriseDN,
|
|
wszDSCACERTATTRIBUTE,
|
|
FALSE, // fDelete
|
|
&dwDisp,
|
|
&pwszError);
|
|
_JumpIfErrorStr(hr, error, "AddCertToAttribute", pwszEnterpriseDN);
|
|
|
|
CSILOG(S_OK, IDS_ILOG_CREATENTAUTHTRUST, pwszEnterpriseDN, NULL, NULL);
|
|
}
|
|
|
|
error:
|
|
CSILOG(hr, IDS_ILOG_CREATEROOTTRUST, pwszRootDN, pwszError, NULL);
|
|
if (NULL != pwszError)
|
|
{
|
|
LocalFree(pwszError);
|
|
}
|
|
if (NULL != pwszEnterpriseDN)
|
|
{
|
|
LocalFree(pwszEnterpriseDN);
|
|
}
|
|
if (NULL != pwszRootDN)
|
|
{
|
|
LocalFree(pwszRootDN);
|
|
}
|
|
myLdapClose(pld, strDomainDN, strConfig);
|
|
if (NULL != pNTAuthSD)
|
|
{
|
|
LocalFree(pNTAuthSD);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
#define wszCOLON L":"
|
|
|
|
// Suppress FILE URLs if a DS is available, as LDAP access within the
|
|
// enterprise should suffice, and http: should work outside the enterprise.
|
|
// Certs with too many URLs don't always fit on smart cards.
|
|
|
|
#define wszCRLPATHDEFAULT \
|
|
wszCERTENROLLSHAREPATH \
|
|
L"\\" \
|
|
wszFCSAPARM_SANITIZEDCANAME \
|
|
wszFCSAPARM_CRLFILENAMESUFFIX \
|
|
wszFCSAPARM_CRLDELTAFILENAMESUFFIX \
|
|
L".crl"
|
|
|
|
CSURLTEMPLATE const s_aRevURL[] = {
|
|
{
|
|
CSURL_SERVERPUBLISH | CSURL_SERVERPUBLISHDELTA | CSURL_ADDSYSTEM32DIR,
|
|
wszCRLPATHDEFAULT,
|
|
},
|
|
{
|
|
CSURL_SERVERPUBLISH | CSURL_SERVERPUBLISHDELTA | CSURL_ADDTOCERTCDP | CSURL_ADDTOFRESHESTCRL | CSURL_ADDTOCRLCDP | CSURL_DSONLY,
|
|
const_cast<WCHAR *>(g_wszzLDAPRevocationURLTemplate),
|
|
},
|
|
{
|
|
CSURL_ADDTOCERTCDP | CSURL_ADDTOFRESHESTCRL,
|
|
const_cast<WCHAR *>(g_wszHTTPRevocationURLTemplate),
|
|
},
|
|
{
|
|
CSURL_ADDTOCERTCDP | CSURL_ADDTOFRESHESTCRL | CSURL_NODS,
|
|
const_cast<WCHAR *>(g_wszFILERevocationURLTemplate),
|
|
},
|
|
#if 0
|
|
{
|
|
CSURL_SERVERPUBLISH | CSURL_SERVERPUBLISHDELTA | CSURL_DSONLY,
|
|
const_cast<WCHAR *>(g_wszCDPDNTemplate),
|
|
},
|
|
#endif
|
|
{ 0, NULL }
|
|
};
|
|
|
|
|
|
#define wszCACERTPATHDEFAULT \
|
|
wszCERTENROLLSHAREPATH \
|
|
L"\\" \
|
|
wszFCSAPARM_SERVERDNSNAME \
|
|
L"_" \
|
|
wszFCSAPARM_SANITIZEDCANAME \
|
|
wszFCSAPARM_CERTFILENAMESUFFIX \
|
|
L".crt"
|
|
|
|
CSURLTEMPLATE const s_aCACertURL[] = {
|
|
{
|
|
CSURL_SERVERPUBLISH | CSURL_ADDSYSTEM32DIR,
|
|
wszCACERTPATHDEFAULT,
|
|
},
|
|
{
|
|
CSURL_SERVERPUBLISH | CSURL_ADDTOCERTCDP | CSURL_DSONLY,
|
|
const_cast<WCHAR *>(g_wszzLDAPIssuerCertURLTemplate),
|
|
},
|
|
{
|
|
CSURL_ADDTOCERTCDP,
|
|
const_cast<WCHAR *>(g_wszHTTPIssuerCertURLTemplate),
|
|
},
|
|
{
|
|
CSURL_ADDTOCERTCDP | CSURL_NODS,
|
|
const_cast<WCHAR *>(g_wszFILEIssuerCertURLTemplate),
|
|
},
|
|
#if 0
|
|
{
|
|
CSURL_SERVERPUBLISH | CSURL_DSONLY,
|
|
const_cast<WCHAR *>(g_wszAIADNTemplate),
|
|
},
|
|
#endif
|
|
{ 0, NULL }
|
|
};
|
|
|
|
#define CSURL_DSDEPENDENT \
|
|
(CSURL_SERVERPUBLISH | \
|
|
CSURL_SERVERPUBLISHDELTA | \
|
|
CSURL_ADDTOCERTCDP | \
|
|
CSURL_ADDTOFRESHESTCRL)
|
|
|
|
|
|
HRESULT
|
|
GetPublicationURLTemplates(
|
|
IN CSURLTEMPLATE const *aTemplate,
|
|
IN BOOL fUseDS,
|
|
IN WCHAR const *pwszSystem32,
|
|
OUT WCHAR **ppwszz)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszz;
|
|
DWORD cwc;
|
|
CSURLTEMPLATE const *pTemplate;
|
|
WCHAR awc[cwcDWORDSPRINTF];
|
|
DWORD Flags;
|
|
|
|
*ppwszz = NULL;
|
|
cwc = 1; // final trailing L'\0'
|
|
|
|
for (pTemplate = aTemplate; NULL != pTemplate->pwszURL; pTemplate++)
|
|
{
|
|
Flags = ~CSURL_INITMASK & pTemplate->Flags;
|
|
if ((!fUseDS && (CSURL_DSONLY & pTemplate->Flags)) ||
|
|
(fUseDS && (CSURL_NODS & pTemplate->Flags)))
|
|
{
|
|
Flags &= ~CSURL_DSDEPENDENT;
|
|
}
|
|
cwc += wsprintf(awc, L"%u", Flags);
|
|
cwc += WSZARRAYSIZE(wszCOLON);
|
|
if (CSURL_ADDSYSTEM32DIR & pTemplate->Flags)
|
|
{
|
|
cwc += wcslen(pwszSystem32);
|
|
}
|
|
cwc += wcslen(pTemplate->pwszURL);
|
|
cwc += 1; // trailing L'\0'
|
|
}
|
|
|
|
pwszz = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR));
|
|
if (NULL == pwszz)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
*ppwszz = pwszz;
|
|
|
|
for (pTemplate = aTemplate; NULL != pTemplate->pwszURL; pTemplate++)
|
|
{
|
|
Flags = ~CSURL_INITMASK & pTemplate->Flags;
|
|
if ((!fUseDS && (CSURL_DSONLY & pTemplate->Flags)) ||
|
|
(fUseDS && (CSURL_NODS & pTemplate->Flags)))
|
|
{
|
|
Flags &= ~CSURL_DSDEPENDENT;
|
|
}
|
|
DBGPRINT((
|
|
DBG_SS_CERTLIB,
|
|
"URL Template: %x %x:%ws\n",
|
|
Flags,
|
|
pTemplate->Flags,
|
|
pTemplate->pwszURL));
|
|
|
|
wsprintf(pwszz, L"%u", Flags);
|
|
wcscat(pwszz, wszCOLON);
|
|
if (CSURL_ADDSYSTEM32DIR & pTemplate->Flags)
|
|
{
|
|
wcscat(pwszz, pwszSystem32);
|
|
}
|
|
wcscat(pwszz, pTemplate->pwszURL);
|
|
pwszz += wcslen(pwszz) + 1; // skip L'\0'
|
|
}
|
|
|
|
*pwszz = L'\0';
|
|
CSASSERT(cwc == (DWORD) (pwszz - *ppwszz + 1));
|
|
|
|
#ifdef DBG_CERTSRV_DEBUG_PRINT
|
|
{
|
|
DWORD i = 0;
|
|
WCHAR const *pwsz;
|
|
|
|
for (pwsz = *ppwszz; L'\0' != *pwsz; pwsz += wcslen(pwsz) + 1)
|
|
{
|
|
DBGPRINT((
|
|
DBG_SS_CERTLIB,
|
|
"URL Template[%u]: %ws\n",
|
|
i,
|
|
pwsz));
|
|
i++;
|
|
}
|
|
}
|
|
#endif // DBG_CERTSRV_DEBUG_PRINT
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiGetCRLPublicationURLTemplates(
|
|
IN BOOL fUseDS,
|
|
IN WCHAR const *pwszSystem32,
|
|
OUT WCHAR **ppwszz)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = GetPublicationURLTemplates(s_aRevURL, fUseDS, pwszSystem32, ppwszz);
|
|
_JumpIfError(hr, error, "GetPublicationURLTemplates");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiGetCACertPublicationURLTemplates(
|
|
IN BOOL fUseDS,
|
|
IN WCHAR const *pwszSystem32,
|
|
OUT WCHAR **ppwszz)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = GetPublicationURLTemplates(s_aCACertURL, fUseDS, pwszSystem32, ppwszz);
|
|
_JumpIfError(hr, error, "GetPublicationURLTemplates");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiSetupCAInDS(
|
|
IN WCHAR const *pwszCAServer,
|
|
IN WCHAR const *pwszSanitizedCAName,
|
|
IN WCHAR const *pwszCADisplayName,
|
|
IN BOOL fLoadDefaultTemplates,
|
|
IN ENUM_CATYPES caType,
|
|
IN DWORD iCert,
|
|
IN DWORD iCRL,
|
|
IN BOOL fRenew,
|
|
IN CERT_CONTEXT const *pCert)
|
|
{
|
|
HRESULT hr;
|
|
HCAINFO hCAInfo = NULL;
|
|
WCHAR *pCAProp[2];
|
|
WCHAR *pCertSubjectString = NULL;
|
|
|
|
CAutoLPWSTR wszNameBuffer;
|
|
WCHAR wszDomainNameBuffer[MAX_PATH];
|
|
DWORD cDomainNameBuffer;
|
|
DWORD cbSid;
|
|
BYTE pSid[MAX_SID_LEN];
|
|
SID_NAME_USE SidUse;
|
|
WCHAR *pwszStringSid = NULL;
|
|
PSECURITY_DESCRIPTOR pContainerSD = NULL;
|
|
PSECURITY_DESCRIPTOR pCDPSD = NULL;
|
|
|
|
CERT_CONTEXT const *pCertForDS = NULL;
|
|
WCHAR *pwszSanitizedDSName = NULL;
|
|
|
|
hr = mySanitizedNameToDSName(pwszSanitizedCAName, &pwszSanitizedDSName);
|
|
_JumpIfError(hr, error, "mySanitizedNameToDSName");
|
|
|
|
// Get the SID of the local machine.
|
|
|
|
hr = myGetComputerObjectName(NameSamCompatible, &wszNameBuffer);
|
|
_JumpIfError(hr, error, "myGetComputerObjectName");
|
|
|
|
DBGPRINT((DBG_SS_CERTLIB, "GetComputerObjectName: '%ws'\n", wszNameBuffer));
|
|
|
|
cbSid = sizeof(pSid);
|
|
cDomainNameBuffer = ARRAYSIZE(wszDomainNameBuffer);
|
|
|
|
if (!LookupAccountName(NULL,
|
|
wszNameBuffer,
|
|
pSid,
|
|
&cbSid,
|
|
wszDomainNameBuffer,
|
|
&cDomainNameBuffer,
|
|
&SidUse))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpErrorStr(hr, error, "LookupAccountName", wszNameBuffer);
|
|
}
|
|
|
|
if (!myConvertSidToStringSid(pSid, &pwszStringSid))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myConvertSidToStringSid");
|
|
}
|
|
|
|
// get default DS CDP security descriptor
|
|
hr = myGetSDFromTemplate(WSZ_DEFAULT_CDP_DS_SECURITY,
|
|
pwszStringSid,
|
|
&pCDPSD);
|
|
_JumpIfError(hr, error, "myGetSDFromTemplate");
|
|
|
|
// get default DS AIA security descriptor
|
|
hr = myGetSDFromTemplate(WSZ_DEFAULT_CA_DS_SECURITY,
|
|
NULL,
|
|
&pContainerSD);
|
|
_JumpIfError(hr, error, "myGetSDFromTemplate");
|
|
|
|
hr = CreateEnterpriseAndRootEntry(
|
|
pwszSanitizedDSName,
|
|
pCert,
|
|
caType,
|
|
pContainerSD,
|
|
pContainerSD);
|
|
_JumpIfError(hr, error, "CreateEnterpriseAndRootEntry");
|
|
|
|
|
|
hr = CreateCDPAndAIAAndKRAEntry(
|
|
pwszSanitizedCAName,
|
|
pwszCAServer,
|
|
iCert,
|
|
iCRL,
|
|
pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded,
|
|
pCDPSD,
|
|
pContainerSD);
|
|
_JumpIfError(hr, error, "CreateCDPAndAIAAndKRAEntry");
|
|
|
|
|
|
// Add enterprise
|
|
// service publish entry
|
|
|
|
hr = CAFindByName(
|
|
pwszSanitizedDSName,
|
|
NULL,
|
|
CA_FIND_INCLUDE_UNTRUSTED | CA_FIND_INCLUDE_NON_TEMPLATE_CA,
|
|
&hCAInfo);
|
|
if (S_OK != hr || NULL == hCAInfo)
|
|
{
|
|
hCAInfo = NULL;
|
|
fRenew = FALSE; // recreate security settings, etc.
|
|
|
|
hr = CACreateNewCA(pwszSanitizedDSName, NULL, NULL, &hCAInfo);
|
|
_JumpIfError(hr, error, "CACreateNewCA");
|
|
|
|
if (NULL == hCAInfo)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "hCAInfo(NULL)");
|
|
}
|
|
}
|
|
|
|
if (!fRenew)
|
|
{
|
|
pCAProp[0] = const_cast<WCHAR *>(pwszCAServer);
|
|
pCAProp[1] = NULL;
|
|
hr = CASetCAProperty(hCAInfo, CA_PROP_DNSNAME, pCAProp);
|
|
_JumpIfError(hr, error, "CASetCAProperty(CA_PROP_DNSNAME)");
|
|
|
|
pCAProp[0] = const_cast<WCHAR *>(pwszCADisplayName);
|
|
pCAProp[1] = NULL;
|
|
hr = CASetCAProperty(hCAInfo, CA_PROP_DISPLAY_NAME, pCAProp);
|
|
_JumpIfError(hr, error, "CASetCAProperty(CA_PROP_DISPLAY_NAME)");
|
|
|
|
hr = myCertNameToStr(
|
|
X509_ASN_ENCODING,
|
|
&pCert->pCertInfo->Subject,
|
|
CERT_X500_NAME_STR | CERT_NAME_STR_NO_QUOTING_FLAG,
|
|
&pCertSubjectString);
|
|
_JumpIfError(hr, error, "myCertNameToStr");
|
|
|
|
pCAProp[0] = pCertSubjectString;
|
|
pCAProp[1] = NULL;
|
|
hr = CASetCAProperty(hCAInfo, CA_PROP_CERT_DN, pCAProp);
|
|
_JumpIfError(hr, error, "CASetCAProperty(CA_PROP_CERT_DN)");
|
|
|
|
switch (caType)
|
|
{
|
|
case ENUM_ENTERPRISE_ROOTCA:
|
|
hr = CASetCAFlags(hCAInfo, CA_FLAG_SUPPORTS_NT_AUTHENTICATION);
|
|
_JumpIfError(hr, error, "CASetCAFlags");
|
|
|
|
break;
|
|
|
|
case ENUM_ENTERPRISE_SUBCA:
|
|
hr = CASetCAFlags(hCAInfo, CA_FLAG_SUPPORTS_NT_AUTHENTICATION);
|
|
_JumpIfError(hr, error, "CASetCAFlags");
|
|
|
|
break;
|
|
|
|
case ENUM_STANDALONE_ROOTCA:
|
|
hr = CASetCAFlags(hCAInfo, CA_FLAG_NO_TEMPLATE_SUPPORT | CA_FLAG_CA_SUPPORTS_MANUAL_AUTHENTICATION);
|
|
_JumpIfError(hr, error, "CASetCAFlags");
|
|
break;
|
|
|
|
case ENUM_STANDALONE_SUBCA:
|
|
hr = CASetCAFlags(hCAInfo, CA_FLAG_NO_TEMPLATE_SUPPORT | CA_FLAG_CA_SUPPORTS_MANUAL_AUTHENTICATION);
|
|
_JumpIfError(hr, error, "CASetCAFlags");
|
|
break;
|
|
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "Invalid CA Type");
|
|
}
|
|
if (IsEnterpriseCA(caType))
|
|
{
|
|
hr = CASetCAProperty(
|
|
hCAInfo,
|
|
CA_PROP_CERT_TYPES,
|
|
fLoadDefaultTemplates?
|
|
(FIsAdvancedServer()?
|
|
s_apwszCertTypeAdvancedServer :
|
|
s_apwszCertTypeServer):
|
|
s_apwszCertTypeEmpty);
|
|
_JumpIfError(hr, error, "CASetCAProperty(CA_PROP_CERT_TYPES)");
|
|
}
|
|
}
|
|
|
|
// create a new cert context without key prov info
|
|
pCertForDS = CertCreateCertificateContext(X509_ASN_ENCODING,
|
|
pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded);
|
|
if (NULL == pCertForDS)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
|
|
hr = CASetCACertificate(hCAInfo, pCertForDS);
|
|
_JumpIfError(hr, error, "CASetCACertificate");
|
|
|
|
if (!fRenew)
|
|
{
|
|
hr = CASetCASecurity(hCAInfo, pCDPSD);
|
|
_JumpIfError(hr, error, "CASetCASecurity");
|
|
}
|
|
hr = CAUpdateCA(hCAInfo);
|
|
_JumpIfError(hr, error, "CAUpdateCA");
|
|
|
|
error:
|
|
if (NULL != pwszStringSid)
|
|
{
|
|
LocalFree(pwszStringSid);
|
|
}
|
|
if (NULL != pwszSanitizedDSName)
|
|
{
|
|
LocalFree(pwszSanitizedDSName);
|
|
}
|
|
if (NULL != pCertSubjectString)
|
|
{
|
|
LocalFree(pCertSubjectString);
|
|
}
|
|
if (NULL != hCAInfo)
|
|
{
|
|
CACloseCA(hCAInfo);
|
|
}
|
|
if (NULL != pCDPSD)
|
|
{
|
|
LocalFree(pCDPSD);
|
|
}
|
|
if (NULL != pContainerSD)
|
|
{
|
|
LocalFree(pContainerSD);
|
|
}
|
|
if (NULL != pCertForDS)
|
|
{
|
|
CertFreeCertificateContext(pCertForDS);
|
|
}
|
|
CSILOG(hr, IDS_ILOG_PUBLISHCA, NULL, NULL, NULL);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
csiIsAnyDSCAAvailable(VOID)
|
|
{
|
|
// this is an expensive call; cache result
|
|
static BOOL available = FALSE; // static inits to FALSE
|
|
static BOOL fKnowAvailable = FALSE; // static inits to FALSE
|
|
|
|
HCAINFO hCAInfo = NULL;
|
|
|
|
if (!fKnowAvailable)
|
|
{
|
|
HRESULT hr;
|
|
|
|
fKnowAvailable = TRUE;
|
|
|
|
hr = CAEnumFirstCA(
|
|
NULL,
|
|
CA_FIND_INCLUDE_UNTRUSTED | CA_FIND_INCLUDE_NON_TEMPLATE_CA,
|
|
&hCAInfo);
|
|
_JumpIfError(hr, error, "CAEnumFirstCA");
|
|
|
|
if (NULL == hCAInfo)
|
|
{
|
|
goto error;
|
|
}
|
|
available = TRUE;
|
|
}
|
|
|
|
error:
|
|
if (NULL != hCAInfo)
|
|
CACloseCA(hCAInfo);
|
|
|
|
return available;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiSetKeyContainerSecurity(
|
|
IN HCRYPTPROV hProv)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = mySetKeyContainerSecurity(hProv);
|
|
_JumpIfError(hr, error, "mySetKeyContainerSecurity");
|
|
|
|
error:
|
|
CSILOG(hr, IDS_ILOG_SETKEYSECURITY, NULL, NULL, NULL);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiSetAdminOnlyFolderSecurity(
|
|
IN LPCWSTR szFolderPath,
|
|
IN BOOL fAllowEveryoneRead,
|
|
IN BOOL fUseDS)
|
|
{
|
|
HRESULT hr;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
|
|
// choose which access we want to allow
|
|
LPCWSTR pwszDescriptor;
|
|
if (fUseDS)
|
|
if (fAllowEveryoneRead)
|
|
pwszDescriptor = WSZ_DEFAULT_SF_USEDS_EVERYONEREAD_SECURITY;
|
|
else
|
|
pwszDescriptor = WSZ_DEFAULT_SF_USEDS_SECURITY;
|
|
else
|
|
if (fAllowEveryoneRead)
|
|
pwszDescriptor = WSZ_DEFAULT_SF_EVERYONEREAD_SECURITY;
|
|
else
|
|
pwszDescriptor = WSZ_DEFAULT_SF_SECURITY;
|
|
|
|
hr = myGetSDFromTemplate(pwszDescriptor, NULL, &pSD);
|
|
_JumpIfError(hr, error, "myGetSDFromTemplate");
|
|
|
|
if (!SetFileSecurity(
|
|
szFolderPath,
|
|
DACL_SECURITY_INFORMATION,
|
|
pSD))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "SetFileSecurity");
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pSD)
|
|
{
|
|
LocalFree(pSD);
|
|
}
|
|
CSILOG(hr, IDS_ILOG_SETADMINONLYFOLDERSECURITY, szFolderPath, NULL, NULL);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiSaveCertAndKeys(
|
|
IN CERT_CONTEXT const *pCert,
|
|
IN HCERTSTORE hAdditionalStore,
|
|
IN CRYPT_KEY_PROV_INFO const *pkpi,
|
|
IN ENUM_CATYPES CAType)
|
|
{
|
|
HRESULT hr;
|
|
CERT_CHAIN_CONTEXT const *pCertChain = NULL;
|
|
CERT_CHAIN_PARA CertChainPara;
|
|
HCERTSTORE hNTAuthStore = NULL;
|
|
|
|
ZeroMemory(&CertChainPara, sizeof(CertChainPara));
|
|
CertChainPara.cbSize = sizeof(CertChainPara);
|
|
|
|
if (!CertGetCertificateChain(
|
|
HCCE_LOCAL_MACHINE,
|
|
pCert,
|
|
NULL,
|
|
hAdditionalStore,
|
|
&CertChainPara,
|
|
0,
|
|
NULL,
|
|
&pCertChain))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateChain");
|
|
}
|
|
|
|
// make sure there is at least 1 simple chain
|
|
|
|
if (0 == pCertChain->cChain)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
_JumpError(hr, error, "pCertChain->cChain");
|
|
}
|
|
|
|
hr = mySaveChainAndKeys(
|
|
pCertChain->rgpChain[0],
|
|
wszMY_CERTSTORE,
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE,
|
|
pkpi,
|
|
NULL);
|
|
_JumpIfError(hr, error, "mySaveChainAndKeys");
|
|
|
|
if (IsEnterpriseCA(CAType))
|
|
{
|
|
hNTAuthStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_REGISTRY_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
|
|
wszNTAUTH_CERTSTORE);
|
|
if (NULL == hNTAuthStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpErrorStr(hr, error, "CertOpenStore", wszNTAUTH_CERTSTORE);
|
|
}
|
|
|
|
if (!CertAddEncodedCertificateToStore(
|
|
hNTAuthStore,
|
|
X509_ASN_ENCODING,
|
|
pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
NULL))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertAddEncodedCertificateToStore");
|
|
}
|
|
}
|
|
|
|
error:
|
|
if (pCertChain != NULL)
|
|
{
|
|
CertFreeCertificateChain(pCertChain);
|
|
}
|
|
if (NULL != hNTAuthStore)
|
|
{
|
|
CertCloseStore(hNTAuthStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
AddCertBlobToMemAndRootStores(
|
|
IN OUT HCERTSTORE hStore,
|
|
IN BYTE const *pb,
|
|
IN DWORD cb)
|
|
{
|
|
HRESULT hr;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
HCERTSTORE hStoreRoot = NULL;
|
|
BOOL fRoot;
|
|
|
|
pCert = CertCreateCertificateContext(X509_ASN_ENCODING, pb, cb);
|
|
if (NULL == pCert)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
if (!CertAddCertificateContextToStore(
|
|
hStore,
|
|
pCert,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
NULL))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertAddCertificateContextToStore");
|
|
}
|
|
|
|
fRoot = CertCompareCertificateName(
|
|
X509_ASN_ENCODING,
|
|
&pCert->pCertInfo->Subject,
|
|
&pCert->pCertInfo->Issuer);
|
|
if (fRoot)
|
|
{
|
|
hStoreRoot = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_REGISTRY_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE |
|
|
CERT_STORE_ENUM_ARCHIVED_FLAG,
|
|
wszROOT_CERTSTORE);
|
|
if (NULL == hStoreRoot)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
if (!CertAddCertificateContextToStore(
|
|
hStoreRoot,
|
|
pCert,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
NULL))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertAddCertificateContextToStore");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pCert)
|
|
{
|
|
CertFreeCertificateContext(pCert);
|
|
}
|
|
if (NULL != hStoreRoot)
|
|
{
|
|
CertCloseStore(hStoreRoot, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
LoadMissingCertBlob(
|
|
IN OUT HCERTSTORE hStore,
|
|
IN BYTE const *pb,
|
|
IN DWORD cb)
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbDecoded = NULL;
|
|
DWORD cbDecoded;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
HCERTSTORE hStorePKCS7 = NULL;
|
|
BOOL fTryPKCS7 = TRUE;
|
|
|
|
if (myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_CERT_TO_BE_SIGNED,
|
|
pb,
|
|
cb,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pbDecoded,
|
|
&cbDecoded))
|
|
{
|
|
hr = AddCertBlobToMemAndRootStores(hStore, pb, cb);
|
|
_JumpIfError(hr, error, "AddCertBlobToMemAndRootStores");
|
|
|
|
fTryPKCS7 = FALSE;
|
|
}
|
|
else
|
|
if (myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
PKCS_CONTENT_INFO_SEQUENCE_OF_ANY,
|
|
pb,
|
|
cb,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pbDecoded,
|
|
&cbDecoded))
|
|
{
|
|
CRYPT_CONTENT_INFO_SEQUENCE_OF_ANY const *pSeq;
|
|
DWORD iCert;
|
|
|
|
pSeq = (CRYPT_CONTENT_INFO_SEQUENCE_OF_ANY const *) pbDecoded;
|
|
|
|
if (0 == strcmp(szOID_NETSCAPE_CERT_SEQUENCE, pSeq->pszObjId))
|
|
{
|
|
fTryPKCS7 = FALSE;
|
|
for (iCert = 0; iCert < pSeq->cValue; iCert++)
|
|
{
|
|
hr = AddCertBlobToMemAndRootStores(
|
|
hStore,
|
|
pSeq->rgValue[iCert].pbData,
|
|
pSeq->rgValue[iCert].cbData);
|
|
_JumpIfError(hr, error, "AddCertBlobToMemAndRootStores");
|
|
}
|
|
}
|
|
}
|
|
if (fTryPKCS7)
|
|
{
|
|
CRYPT_DATA_BLOB blobPKCS7;
|
|
|
|
blobPKCS7.pbData = const_cast<BYTE *>(pb);
|
|
blobPKCS7.cbData = cb;
|
|
|
|
hStorePKCS7 = CertOpenStore(
|
|
CERT_STORE_PROV_PKCS7,
|
|
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
NULL, // hCryptProv
|
|
0, // dwFlags
|
|
&blobPKCS7);
|
|
if (NULL == hStorePKCS7)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
for (;;)
|
|
{
|
|
pCert = CertEnumCertificatesInStore(hStorePKCS7, pCert);
|
|
if (NULL == pCert)
|
|
{
|
|
break;
|
|
}
|
|
hr = AddCertBlobToMemAndRootStores(
|
|
hStore,
|
|
pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded);
|
|
_JumpIfError(hr, error, "AddCertBlobToMemAndRootStores");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pbDecoded)
|
|
{
|
|
LocalFree(pbDecoded);
|
|
}
|
|
if (NULL != pCert)
|
|
{
|
|
CertFreeCertificateContext(pCert);
|
|
}
|
|
if (NULL != hStorePKCS7)
|
|
{
|
|
CertCloseStore(hStorePKCS7, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
LoadMissingCert(
|
|
IN HINSTANCE hInstance,
|
|
IN HWND hwnd,
|
|
IN OUT HCERTSTORE hStore,
|
|
IN OPTIONAL WCHAR const *pwszMissingIssuer)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszFile = NULL;
|
|
BYTE *pb = NULL;
|
|
DWORD cb;
|
|
|
|
hr = myGetOpenFileNameEx(
|
|
hwnd,
|
|
hInstance,
|
|
IDS_CAHIER_INSTALL_MISIINGCERT_TITLE,
|
|
pwszMissingIssuer,
|
|
IDS_CAHIER_CERTFILE_FILTER,
|
|
0, // no def ext
|
|
OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY,
|
|
NULL, // no default file
|
|
&pwszFile);
|
|
if (S_OK == hr && NULL == pwszFile)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
_JumpIfError(hr, error, "myGetOpenFileName");
|
|
|
|
hr = DecodeFileW(pwszFile, &pb, &cb, CRYPT_STRING_ANY);
|
|
_JumpIfError(hr, error, "DecodeFileW");
|
|
|
|
hr = LoadMissingCertBlob(hStore, pb, cb);
|
|
_JumpIfError(hr, error, "LoadMissingCertBlob");
|
|
|
|
error:
|
|
if (NULL != pb)
|
|
{
|
|
LocalFree(pb);
|
|
}
|
|
if (NULL != pwszFile)
|
|
{
|
|
LocalFree(pwszFile);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
InstallCAChain(
|
|
IN HINSTANCE hInstance,
|
|
IN BOOL fUnattended,
|
|
IN HWND hwnd,
|
|
IN PCCERT_CONTEXT pCert,
|
|
IN CRYPT_KEY_PROV_INFO const *pKeyProvInfo,
|
|
IN ENUM_CATYPES CAType,
|
|
OPTIONAL IN BYTE const *pbChain,
|
|
IN DWORD cbChain)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszMissingIssuer = NULL;
|
|
HCERTSTORE hTempMemoryStore = NULL;
|
|
|
|
if (IsSubordinateCA(CAType))
|
|
{
|
|
hTempMemoryStore = CertOpenStore(
|
|
CERT_STORE_PROV_MEMORY,
|
|
X509_ASN_ENCODING,
|
|
NULL,
|
|
0,
|
|
NULL);
|
|
if (NULL == hTempMemoryStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenSystemStore");
|
|
}
|
|
|
|
if (NULL != pbChain)
|
|
{
|
|
hr = LoadMissingCertBlob(hTempMemoryStore, pbChain, cbChain);
|
|
_JumpIfError(hr, error, "LoadMissingCertBlob");
|
|
}
|
|
|
|
// see if CA chain can be built
|
|
|
|
for (;;)
|
|
{
|
|
if (NULL != pwszMissingIssuer)
|
|
{
|
|
LocalFree(pwszMissingIssuer);
|
|
pwszMissingIssuer = NULL;
|
|
}
|
|
hr = myVerifyCertContext(
|
|
pCert, // pCert
|
|
0, // dwFlags
|
|
0, // cUsageOids
|
|
NULL, // apszUsageOids
|
|
HCCE_LOCAL_MACHINE, // hChainEngine
|
|
hTempMemoryStore, // hAdditionalStore
|
|
&pwszMissingIssuer);
|
|
if (S_OK != hr)
|
|
{
|
|
if (NULL != pwszMissingIssuer)
|
|
{
|
|
if (IDCANCEL == CertMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_INCOMPLETECHAIN,
|
|
hr,
|
|
MB_OKCANCEL | MB_ICONWARNING,
|
|
pwszMissingIssuer) ||
|
|
fUnattended)
|
|
{
|
|
_JumpError(hr, error, "cannot build CA chain");
|
|
}
|
|
|
|
hr = LoadMissingCert(
|
|
hInstance,
|
|
hwnd,
|
|
hTempMemoryStore,
|
|
pwszMissingIssuer);
|
|
_PrintIfError(hr, "LoadMissingCert");
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// recommend not continue
|
|
|
|
if (IDCANCEL == CertMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
CERT_E_UNTRUSTEDROOT == hr?
|
|
IDS_ERR_UNTRUSTEDROOT :
|
|
IDS_ERR_INVALIDCHAIN,
|
|
hr,
|
|
MB_OKCANCEL | MB_ICONWARNING,
|
|
NULL))
|
|
{
|
|
_JumpError(hr, error, "cannot verify CA chain");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = csiSaveCertAndKeys(pCert, hTempMemoryStore, pKeyProvInfo, CAType);
|
|
if (S_OK != hr)
|
|
{
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_CERTADDCERTIFICATECONTEXTTOSTORE,
|
|
hr,
|
|
NULL);
|
|
_JumpError(hr, error, "csiSaveCertAndKeys");
|
|
}
|
|
|
|
error:
|
|
if (NULL != pwszMissingIssuer)
|
|
{
|
|
LocalFree(pwszMissingIssuer);
|
|
}
|
|
if (NULL != hTempMemoryStore)
|
|
{
|
|
CertCloseStore(hTempMemoryStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
VerifyPublicKeyMatch(
|
|
IN CERT_CONTEXT const *pCert,
|
|
IN CRYPT_KEY_PROV_INFO const *pKeyProvInfo)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = myVerifyPublicKey(
|
|
NULL, // pCert
|
|
FALSE, // fV1Cert
|
|
pKeyProvInfo,
|
|
&pCert->pCertInfo->SubjectPublicKeyInfo,
|
|
NULL); // pfMatchingKey
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
BuildCAChainFromCert(
|
|
IN HINSTANCE hInstance,
|
|
IN BOOL fUnattended,
|
|
IN HWND hwnd,
|
|
IN CRYPT_KEY_PROV_INFO const *pKeyProvInfo,
|
|
IN WCHAR const *pwszSanitizedCAName,
|
|
IN WCHAR const *pwszCommonName,
|
|
IN const ENUM_CATYPES CAType,
|
|
IN DWORD iCert,
|
|
IN DWORD iCRL,
|
|
OPTIONAL IN BYTE const *pbChain,
|
|
IN DWORD cbChain,
|
|
IN CERT_CONTEXT const *pCert)
|
|
{
|
|
HRESULT hr;
|
|
UINT idserr;
|
|
CERT_NAME_INFO *pNameInfo = NULL;
|
|
DWORD cbNameInfo;
|
|
WCHAR const *pwszCN;
|
|
HCERTSTORE hMyStore = NULL;
|
|
CERT_CONTEXT const *pccPrevious = NULL;
|
|
|
|
// make sure the cert file matches current ca name
|
|
|
|
if (!myDecodeName(
|
|
X509_ASN_ENCODING,
|
|
X509_UNICODE_NAME,
|
|
pCert->pCertInfo->Subject.pbData,
|
|
pCert->pCertInfo->Subject.cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pNameInfo,
|
|
&cbNameInfo))
|
|
{
|
|
hr = myHLastError();
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_MYDECODENAME,
|
|
hr,
|
|
NULL);
|
|
_JumpError(hr, error, "myDecodeName");
|
|
}
|
|
|
|
hr = myGetCertNameProperty(FALSE, pNameInfo, szOID_COMMON_NAME, &pwszCN);
|
|
_PrintIfError(hr, "myGetCertNameProperty");
|
|
|
|
if (S_OK == hr && 0 != lstrcmp(pwszCommonName, pwszCN))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_PrintErrorStr(hr, "lstrcmp", pwszCN);
|
|
}
|
|
idserr = IDS_ERR_NOT_MATCH_COMMONNAME;
|
|
|
|
// If renewing and reusing the old key, verify the binary subject matches
|
|
// the previous cert subject, to prevent CRL Issuer and cert Issuer
|
|
// mismatches when verifying chains.
|
|
|
|
if (S_OK == hr && 0 < iCert && iCert != iCRL)
|
|
{
|
|
DWORD NameId;
|
|
|
|
hMyStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE |
|
|
CERT_STORE_MAXIMUM_ALLOWED_FLAG |
|
|
CERT_STORE_READONLY_FLAG,
|
|
wszMY_CERTSTORE);
|
|
if (NULL == hMyStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
hr = myFindCACertByHashIndex(
|
|
hMyStore,
|
|
pwszSanitizedCAName,
|
|
CSRH_CASIGCERT,
|
|
iCert - 1,
|
|
&NameId,
|
|
&pccPrevious);
|
|
_PrintIfError(hr, "myFindCACertByHashIndex");
|
|
|
|
if (S_OK == hr &&
|
|
!CertCompareCertificateName(
|
|
X509_ASN_ENCODING,
|
|
&pCert->pCertInfo->Subject,
|
|
&pccPrevious->pCertInfo->Subject))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
idserr = IDS_ERR_NOT_MATCH_BINARYNAME;
|
|
_PrintErrorStr(hr, "CertCompareCertificateName", pwszCN);
|
|
}
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
idserr,
|
|
hr,
|
|
NULL);
|
|
_JumpError(hr, error, "cert common/binary name mismatch");
|
|
}
|
|
|
|
hr = myVerifyPublicKey(
|
|
NULL, // pCert
|
|
FALSE, // fV1Cert
|
|
pKeyProvInfo,
|
|
&pCert->pCertInfo->SubjectPublicKeyInfo,
|
|
NULL); // pfMatchingKey
|
|
if (S_OK != hr)
|
|
{
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_NOT_MATCH_KEY,
|
|
hr,
|
|
NULL);
|
|
_JumpError(hr, error, "myVerifyPublicKey");
|
|
}
|
|
hr = IsCACert(hInstance, fUnattended, hwnd, pCert, CAType);
|
|
_JumpIfError(hr, error, "IsCACert");
|
|
|
|
hr = InstallCAChain(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
pCert,
|
|
pKeyProvInfo,
|
|
CAType,
|
|
pbChain,
|
|
cbChain);
|
|
_JumpIfError(hr, error, "InstallCAChain");
|
|
|
|
error:
|
|
if (NULL != pccPrevious)
|
|
{
|
|
CertFreeCertificateContext(pccPrevious);
|
|
}
|
|
if (NULL != hMyStore)
|
|
{
|
|
CertCloseStore(hMyStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
if (NULL != pNameInfo)
|
|
{
|
|
LocalFree(pNameInfo);
|
|
}
|
|
CSILOG(hr, IDS_ILOG_SAVECERTANDKEYS, NULL, NULL, NULL);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiFinishInstallationFromPKCS7(
|
|
IN HINSTANCE hInstance,
|
|
IN BOOL fUnattended,
|
|
IN HWND hwnd,
|
|
IN WCHAR const *pwszSanitizedCAName,
|
|
IN WCHAR const *pwszCACommonName,
|
|
IN CRYPT_KEY_PROV_INFO const *pKeyProvInfo,
|
|
IN ENUM_CATYPES CAType,
|
|
IN DWORD iCert,
|
|
IN DWORD iCRL,
|
|
IN BOOL fUseDS,
|
|
IN BOOL fRenew,
|
|
IN WCHAR const *pwszServerName,
|
|
IN BYTE const *pbChainOrCert,
|
|
IN DWORD cbChainOrCert,
|
|
OPTIONAL IN WCHAR const *pwszCACertFile)
|
|
{
|
|
HRESULT hr;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
WCHAR *pwszWebCACertFile = NULL;
|
|
WCHAR *pwszKeyContainer = NULL;
|
|
WCHAR wszTemp[MAX_PATH];
|
|
WCHAR wszBuffer[MAX_PATH];
|
|
DWORD NameId;
|
|
WCHAR *pwszRequestFile = NULL;
|
|
DWORD cwc;
|
|
|
|
hr = E_FAIL;
|
|
if (!IsRootCA(CAType)) // skip PKCS7 code for known raw X509 root cert
|
|
{
|
|
hr = ExtractCACertFromPKCS7(
|
|
pwszCACommonName,
|
|
pbChainOrCert,
|
|
cbChainOrCert,
|
|
&pCert);
|
|
_PrintIfError(hr, "ExtractCACertFromPKCS7");
|
|
}
|
|
|
|
if (NULL == pCert)
|
|
{
|
|
pCert = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
pbChainOrCert,
|
|
cbChainOrCert);
|
|
if (NULL == pCert)
|
|
{
|
|
hr = myHLastError();
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_CERTCREATECERTIFICATECONTEXT,
|
|
hr,
|
|
NULL);
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
pbChainOrCert = NULL; // Don't need to process this cert any further
|
|
}
|
|
|
|
hr = myGetNameId(pCert, &NameId);
|
|
_PrintIfError(hr, "myGetNameId");
|
|
if (S_OK == hr && MAKECANAMEID(iCert, iCRL) != NameId)
|
|
{
|
|
// get request file name
|
|
|
|
hr = csiGetCARequestFileName(
|
|
hInstance,
|
|
hwnd,
|
|
pwszSanitizedCAName,
|
|
iCert,
|
|
iCRL,
|
|
&pwszRequestFile);
|
|
_PrintIfError(hr, "csiGetCARequestFileName");
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_RENEWEDCERTCAVERSION,
|
|
hr,
|
|
pwszRequestFile);
|
|
_JumpError(hr, error, "CA Version");
|
|
}
|
|
|
|
// build a chain and install it
|
|
|
|
hr = BuildCAChainFromCert(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
pKeyProvInfo,
|
|
pwszSanitizedCAName,
|
|
pwszCACommonName,
|
|
CAType,
|
|
iCert,
|
|
iCRL,
|
|
pbChainOrCert,
|
|
cbChainOrCert,
|
|
pCert);
|
|
_JumpIfError(hr, error, "BuildCAChainFromCert");
|
|
|
|
// store CA cert hash
|
|
|
|
hr = mySetCARegHash(pwszSanitizedCAName, CSRH_CASIGCERT, iCert, pCert);
|
|
_JumpIfError(hr, error, "mySetCARegHash");
|
|
|
|
if (fUseDS)
|
|
{
|
|
// save in ds
|
|
hr = csiSetupCAInDS(
|
|
pwszServerName,
|
|
pwszSanitizedCAName,
|
|
pwszCACommonName,
|
|
TRUE,
|
|
CAType,
|
|
iCert,
|
|
iCRL,
|
|
fRenew,
|
|
pCert);
|
|
_JumpIfError(hr, error, "csiSetupCAInDS");
|
|
}
|
|
|
|
if (NULL != pwszCACertFile)
|
|
{
|
|
// write this CA cert into shared folder
|
|
|
|
if (!DeleteFile(pwszCACertFile))
|
|
{
|
|
hr = myHLastError();
|
|
_PrintErrorStr2(
|
|
hr,
|
|
"DeleteFile",
|
|
pwszCACertFile,
|
|
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
|
|
}
|
|
if (!csiWriteDERToFile(
|
|
pwszCACertFile,
|
|
(BYTE *) pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded,
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd))
|
|
{
|
|
hr = myHLastError();
|
|
_PrintErrorStr(hr, "csiWriteDERToFile", pwszCACertFile);
|
|
}
|
|
}
|
|
|
|
// write cert file for web pages
|
|
|
|
cwc = GetEnvironmentVariable(L"SystemRoot", wszTemp, ARRAYSIZE(wszTemp));
|
|
if (0 == cwc || ARRAYSIZE(wszTemp) <= cwc)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_ENV_NOT_SET,
|
|
hr,
|
|
NULL);
|
|
_JumpError(hr, error, "GetEnvironmentVariable");
|
|
}
|
|
|
|
if(ARRAYSIZE(wszBuffer)<wcslen(wszTemp)+wcslen(L"\\System32\\")+
|
|
wcslen(wszCERTENROLLSHAREPATH)+1)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
|
|
_JumpError(hr, error, "file name too long");
|
|
}
|
|
|
|
wcscpy(wszBuffer, wszTemp);
|
|
wcscat(wszBuffer, L"\\System32\\" wszCERTENROLLSHAREPATH);
|
|
|
|
hr = csiBuildFileName(
|
|
wszBuffer,
|
|
pwszSanitizedCAName,
|
|
L".crt",
|
|
iCert,
|
|
&pwszWebCACertFile,
|
|
hInstance,
|
|
fUnattended,
|
|
NULL);
|
|
_JumpIfError(hr, error, "csiBuildFileName");
|
|
|
|
hr = EncodeToFileW(
|
|
pwszWebCACertFile,
|
|
pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded,
|
|
DECF_FORCEOVERWRITE | CRYPT_STRING_BINARY);
|
|
if (S_OK != hr)
|
|
{
|
|
CertErrorMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_WRITEDERTOFILE,
|
|
hr,
|
|
pwszWebCACertFile);
|
|
_JumpError(hr, error, "EncodeToFileW");
|
|
}
|
|
|
|
// Set the security on the ds/registry/files etc.
|
|
|
|
if (!fRenew)
|
|
{
|
|
hr = myAllocIndexedName(
|
|
pwszSanitizedCAName,
|
|
iCRL,
|
|
MAXDWORD, // IndexTarget
|
|
&pwszKeyContainer);
|
|
_JumpIfError(hr, error, "myAllocIndexedName");
|
|
|
|
hr = csiInitializeCertSrvSecurity(
|
|
pwszSanitizedCAName,
|
|
fUseDS,
|
|
fUseDS); // set DS security if using DS
|
|
_JumpIfError(hr, error, "csiInitializeCertSrvSecurity");
|
|
}
|
|
|
|
error:
|
|
if (NULL != pCert)
|
|
{
|
|
CertFreeCertificateContext(pCert);
|
|
}
|
|
if (NULL != pwszRequestFile)
|
|
{
|
|
LocalFree(pwszRequestFile);
|
|
}
|
|
if (NULL != pwszKeyContainer)
|
|
{
|
|
LocalFree(pwszKeyContainer);
|
|
}
|
|
if (NULL != pwszWebCACertFile)
|
|
{
|
|
LocalFree(pwszWebCACertFile);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
FormRequestHelpMessage(
|
|
IN HINSTANCE hInstance,
|
|
IN LONG lRequestId,
|
|
IN BSTR bStrMsgFromServer,
|
|
IN WCHAR const *pwszParentConfig,
|
|
OUT WCHAR **ppwszHelpMsg)
|
|
{
|
|
|
|
#define wszHELPNEWLINE L"\n"
|
|
#define wszCOMMASPACE L", "
|
|
|
|
HRESULT hr;
|
|
WCHAR wszRequestIdValue[16];
|
|
WCHAR *pwszMsgConfigPrefix = NULL;
|
|
WCHAR *pwszMsgRequestIdPrefix = NULL;
|
|
WCHAR *pwszHelpMsg = NULL;
|
|
DWORD cwc;
|
|
|
|
*ppwszHelpMsg = NULL;
|
|
|
|
// load some format strings in help msg
|
|
hr = myLoadRCString(hInstance, IDS_MSG_PARENTCA_CONFIG, &pwszMsgConfigPrefix);
|
|
_JumpIfError(hr, error, "myLoadRCString");
|
|
|
|
hr = myLoadRCString(hInstance, IDS_MSG_REQUEST_ID, &pwszMsgRequestIdPrefix);
|
|
_JumpIfError(hr, error, "myLoadRCString");
|
|
|
|
swprintf(wszRequestIdValue, L"%ld", lRequestId);
|
|
|
|
cwc = wcslen(pwszMsgConfigPrefix) +
|
|
wcslen(pwszParentConfig) +
|
|
WSZARRAYSIZE(wszCOMMASPACE) +
|
|
wcslen(pwszMsgRequestIdPrefix) +
|
|
wcslen(wszRequestIdValue) +
|
|
1;
|
|
if (NULL != bStrMsgFromServer)
|
|
{
|
|
cwc += SysStringLen(bStrMsgFromServer) + WSZARRAYSIZE(wszHELPNEWLINE);
|
|
}
|
|
|
|
pwszHelpMsg = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR));
|
|
if (NULL == pwszHelpMsg)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
// form help message
|
|
|
|
pwszHelpMsg[0] = L'\0';
|
|
if (NULL != bStrMsgFromServer)
|
|
{
|
|
wcscpy(pwszHelpMsg, bStrMsgFromServer);
|
|
wcscat(pwszHelpMsg, wszHELPNEWLINE);
|
|
}
|
|
wcscat(pwszHelpMsg, pwszMsgConfigPrefix);
|
|
wcscat(pwszHelpMsg, pwszParentConfig);
|
|
wcscat(pwszHelpMsg, wszCOMMASPACE);
|
|
wcscat(pwszHelpMsg, pwszMsgRequestIdPrefix);
|
|
wcscat(pwszHelpMsg, wszRequestIdValue);
|
|
|
|
CSASSERT(wcslen(pwszHelpMsg) + 1 == cwc);
|
|
|
|
*ppwszHelpMsg = pwszHelpMsg;
|
|
pwszHelpMsg = NULL;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszMsgConfigPrefix)
|
|
{
|
|
LocalFree(pwszMsgConfigPrefix);
|
|
}
|
|
if (NULL != pwszMsgRequestIdPrefix)
|
|
{
|
|
LocalFree(pwszMsgRequestIdPrefix);
|
|
}
|
|
if (NULL != pwszHelpMsg)
|
|
{
|
|
LocalFree(pwszHelpMsg);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
HandleSubmitOrRetrieveNotIssued(
|
|
IN HWND hwnd,
|
|
IN HINSTANCE hInstance,
|
|
IN BOOL fUnattended,
|
|
IN WCHAR const *pwszSanitizedCAName,
|
|
IN WCHAR const *pwszParentCAConfig,
|
|
IN LONG disposition,
|
|
IN BSTR strDispositionMessage,
|
|
IN BOOL fRenew,
|
|
IN DWORD iCert,
|
|
IN LONG requestId,
|
|
IN HRESULT hrSubmit,
|
|
IN HRESULT hrLastStatus,
|
|
IN int iMsgId)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszHelpMsg = NULL;
|
|
DWORD dwStatusDisable;
|
|
DWORD dwStatusEnable;
|
|
BOOL fPopup = FALSE;
|
|
|
|
// form custom message
|
|
|
|
hr = FormRequestHelpMessage(
|
|
hInstance,
|
|
requestId,
|
|
strDispositionMessage,
|
|
pwszParentCAConfig,
|
|
&pwszHelpMsg);
|
|
_JumpIfError(hr, error, "FromRequestHelpMessage");
|
|
|
|
|
|
// Assume suspended install, denied request:
|
|
|
|
dwStatusEnable = SETUP_DENIED_FLAG | SETUP_REQUEST_FLAG;
|
|
if (!fRenew && 0 == iCert)
|
|
{
|
|
dwStatusEnable |= SETUP_SUSPEND_FLAG;
|
|
}
|
|
|
|
// Assume the pending request is denied, don't use online parent any more
|
|
|
|
dwStatusDisable = SETUP_ONLINE_FLAG;
|
|
|
|
// now handle disposition
|
|
|
|
switch (disposition)
|
|
{
|
|
case CR_DISP_UNDER_SUBMISSION:
|
|
// the online request is pending, not denied
|
|
|
|
dwStatusEnable &= ~SETUP_DENIED_FLAG;
|
|
dwStatusEnable |= SETUP_ONLINE_FLAG;
|
|
|
|
// online is still enabled
|
|
|
|
dwStatusDisable &= ~SETUP_ONLINE_FLAG;
|
|
|
|
iMsgId = IDS_ERR_REQUEST_PENDING;
|
|
break;
|
|
|
|
case CR_DISP_DENIED:
|
|
// request id is no good any more
|
|
|
|
hr = myDeleteCertRegValue(
|
|
pwszSanitizedCAName,
|
|
NULL,
|
|
NULL,
|
|
wszREGREQUESTID);
|
|
_PrintIfErrorStr(hr, "myDeleteCertRegValue", wszREGREQUESTID);
|
|
|
|
if (0 == iMsgId)
|
|
{
|
|
iMsgId = IDS_ERR_REQUEST_DENIED;
|
|
}
|
|
break;
|
|
|
|
case CR_DISP_INCOMPLETE:
|
|
iMsgId = IDS_ERR_REQUEST_INCOMPLETE;
|
|
break;
|
|
|
|
case CR_DISP_ERROR:
|
|
iMsgId = IDS_ERR_REQUEST_ERROR;
|
|
break;
|
|
|
|
case CR_DISP_ISSUED_OUT_OF_BAND:
|
|
|
|
// same as pending request, but not denied
|
|
|
|
dwStatusEnable &= ~SETUP_DENIED_FLAG;
|
|
|
|
iMsgId = IDS_ERR_REQUEST_OUTOFBAND;
|
|
break;
|
|
|
|
case CR_DISP_REVOKED:
|
|
iMsgId = IDS_ERR_REQUEST_REVOKED;
|
|
break;
|
|
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "Internal error");
|
|
}
|
|
|
|
if (0 != dwStatusDisable)
|
|
{
|
|
// fix status, unset
|
|
|
|
hr = SetSetupStatus(pwszSanitizedCAName, dwStatusDisable, FALSE);
|
|
_JumpIfError(hr, error, "SetSetupStatus");
|
|
}
|
|
if (0 != dwStatusEnable)
|
|
{
|
|
// fix status, set
|
|
|
|
hr = SetSetupStatus(pwszSanitizedCAName, dwStatusEnable, TRUE);
|
|
_JumpIfError(hr, error, "SetSetupStatus");
|
|
}
|
|
|
|
// pop up a warning for generic error
|
|
CertWarningMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
iMsgId,
|
|
hrLastStatus,
|
|
pwszHelpMsg);
|
|
fPopup = TRUE;
|
|
|
|
// use proper error code
|
|
|
|
if (S_OK == hrSubmit)
|
|
{
|
|
// for any disposition, use not ready as error
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_READY);
|
|
}
|
|
else
|
|
{
|
|
// use submit error
|
|
|
|
hr = hrSubmit;
|
|
}
|
|
|
|
// note, never return S_OK
|
|
|
|
error:
|
|
if (!fPopup)
|
|
{
|
|
// a generic one because we won't have any popup later
|
|
CertWarningMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_SUBMIT_REQUEST_FAIL,
|
|
hr,
|
|
L"");
|
|
}
|
|
if (NULL != pwszHelpMsg)
|
|
{
|
|
LocalFree(pwszHelpMsg);
|
|
}
|
|
CSILOG(hr, IDS_ILOG_RETRIEVECERT, NULL, NULL, NULL);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiSubmitCARequest(
|
|
IN HINSTANCE hInstance,
|
|
IN BOOL fUnattended,
|
|
IN HWND hwnd,
|
|
IN BOOL fRenew,
|
|
IN DWORD iCert,
|
|
IN BOOL fRetrievePending,
|
|
IN WCHAR const *pwszSanitizedCAName,
|
|
IN WCHAR const *pwszParentCAMachine,
|
|
IN WCHAR const *pwszParentCAName,
|
|
IN BYTE const *pbRequest,
|
|
IN DWORD cbRequest,
|
|
OUT BSTR *pbStrChain)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hrSubmit;
|
|
HRESULT hrLastStatus;
|
|
WCHAR *pwszParentCAConfig = NULL;
|
|
LONG disposition = CR_DISP_INCOMPLETE;
|
|
LONG requestId = 0;
|
|
int iMsgId;
|
|
ICertRequest *pICertRequest = NULL;
|
|
BOOL fCoInit = FALSE;
|
|
BSTR bstrConfig = NULL;
|
|
BSTR bstrRequest = NULL;
|
|
BSTR strDispositionMessage = NULL;
|
|
|
|
// register parent ca config
|
|
hr = mySetCertRegStrValue(
|
|
pwszSanitizedCAName,
|
|
NULL,
|
|
NULL,
|
|
wszREGPARENTCAMACHINE,
|
|
pwszParentCAMachine);
|
|
_JumpIfErrorStr(hr, error, "mySetCertRegStrValue", wszREGPARENTCAMACHINE);
|
|
|
|
hr = mySetCertRegStrValue(
|
|
pwszSanitizedCAName,
|
|
NULL,
|
|
NULL,
|
|
wszREGPARENTCANAME,
|
|
pwszParentCAName);
|
|
_JumpIfErrorStr(hr, error, "mySetCertRegStrValue", wszREGPARENTCANAME);
|
|
|
|
if (fRetrievePending)
|
|
{
|
|
// get request id
|
|
|
|
hr = myGetCertRegDWValue(
|
|
pwszSanitizedCAName,
|
|
NULL,
|
|
NULL,
|
|
wszREGREQUESTID,
|
|
(DWORD *) &requestId);
|
|
if (S_OK != hr)
|
|
{
|
|
fRetrievePending = FALSE;
|
|
requestId = 0;
|
|
}
|
|
}
|
|
|
|
hr = CoInitialize(NULL);
|
|
if (S_OK != hr && S_FALSE != hr)
|
|
{
|
|
_JumpError(hr, error, "CoInitialize");
|
|
}
|
|
fCoInit = TRUE;
|
|
|
|
hr = CoCreateInstance(
|
|
CLSID_CCertRequest,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ICertRequest,
|
|
(VOID **) &pICertRequest);
|
|
_JumpIfError(hr, error, "CoCreateInstance");
|
|
|
|
// get config string
|
|
|
|
hr = myFormConfigString(
|
|
pwszParentCAMachine,
|
|
pwszParentCAName,
|
|
&pwszParentCAConfig);
|
|
_JumpIfError(hr, error, "myFormConfigString");
|
|
|
|
// to bstr
|
|
|
|
bstrConfig = SysAllocString(pwszParentCAConfig);
|
|
if (NULL == bstrConfig)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
// request to bstr
|
|
|
|
bstrRequest = SysAllocStringByteLen((CHAR *) pbRequest, cbRequest);
|
|
if (NULL == bstrRequest)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
myDeleteCertRegValue(pwszSanitizedCAName, NULL, NULL, wszREGREQUESTID);
|
|
|
|
{
|
|
CWaitCursor cwait;
|
|
|
|
if (fRetrievePending)
|
|
{
|
|
// retrieve the request
|
|
hr = pICertRequest->RetrievePending(
|
|
requestId,
|
|
bstrConfig,
|
|
&disposition);
|
|
}
|
|
else
|
|
{
|
|
hr = pICertRequest->Submit(
|
|
CR_IN_BINARY | CR_IN_PKCS10,
|
|
bstrRequest,
|
|
NULL,
|
|
bstrConfig,
|
|
&disposition);
|
|
}
|
|
hrSubmit = hr;
|
|
hrLastStatus = hr;
|
|
}
|
|
|
|
hr = pICertRequest->GetDispositionMessage(&strDispositionMessage);
|
|
_PrintIfError(hr, "pICertRequest->GetDispositionMessage");
|
|
|
|
if (S_OK == hrSubmit)
|
|
{
|
|
hr = pICertRequest->GetLastStatus(&hrLastStatus);
|
|
_PrintIfError(hr, "pICertRequest->GetLastStatus");
|
|
}
|
|
|
|
CSILOG(
|
|
hrLastStatus,
|
|
fRetrievePending? IDS_ILOG_RETRIEVEPENDING : IDS_ILOG_SUBMITREQUEST,
|
|
bstrConfig,
|
|
strDispositionMessage,
|
|
(DWORD const *) &disposition);
|
|
|
|
iMsgId = 0;
|
|
if (S_OK != hrSubmit)
|
|
{
|
|
// default to a generic message
|
|
|
|
iMsgId = fRetrievePending?
|
|
IDS_ERR_RETRIEVE_PENDING : IDS_ERR_SUBMIT_REQUEST_FAIL;
|
|
|
|
if (HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER) == hrSubmit)
|
|
{
|
|
iMsgId = IDS_ERR_NOT_ENTERPRISE_USER;
|
|
}
|
|
|
|
// if failed, treat as denied
|
|
|
|
disposition = CR_DISP_DENIED;
|
|
}
|
|
|
|
if (CR_DISP_ISSUED != disposition)
|
|
{
|
|
if (!fRetrievePending)
|
|
{
|
|
pICertRequest->GetRequestId(&requestId);
|
|
}
|
|
hr = mySetCertRegDWValue(
|
|
pwszSanitizedCAName,
|
|
NULL,
|
|
NULL,
|
|
wszREGREQUESTID,
|
|
requestId);
|
|
_JumpIfErrorStr(hr, error, "mySetCertRegDWValue", wszREGREQUESTID);
|
|
|
|
hr = HandleSubmitOrRetrieveNotIssued(
|
|
hwnd,
|
|
hInstance,
|
|
fUnattended,
|
|
pwszSanitizedCAName,
|
|
pwszParentCAConfig,
|
|
disposition,
|
|
strDispositionMessage,
|
|
fRenew,
|
|
iCert,
|
|
requestId,
|
|
hrSubmit,
|
|
hrLastStatus,
|
|
iMsgId);
|
|
|
|
// not issued, always exit with error
|
|
|
|
_JumpError(hr, error, "Cert is not issued");
|
|
}
|
|
|
|
// get pkcs7 chain
|
|
|
|
hr = pICertRequest->GetCertificate(
|
|
CR_OUT_CHAIN | CR_OUT_BINARY,
|
|
pbStrChain);
|
|
_JumpIfError(hr, error, "pICertRequest->GetCertificate");
|
|
|
|
error:
|
|
if (NULL != strDispositionMessage)
|
|
{
|
|
SysFreeString(strDispositionMessage);
|
|
}
|
|
if (NULL != pwszParentCAConfig)
|
|
{
|
|
LocalFree(pwszParentCAConfig);
|
|
}
|
|
if (NULL != bstrConfig)
|
|
{
|
|
SysFreeString(bstrConfig);
|
|
}
|
|
if (NULL != bstrRequest)
|
|
{
|
|
SysFreeString(bstrRequest);
|
|
}
|
|
if (NULL != pICertRequest)
|
|
{
|
|
pICertRequest->Release();
|
|
}
|
|
if (fCoInit)
|
|
{
|
|
CoUninitialize();
|
|
}
|
|
CSILOG(
|
|
hr,
|
|
fRetrievePending? IDS_ILOG_RETRIEVEPENDING : IDS_ILOG_SUBMITREQUEST,
|
|
pwszParentCAMachine,
|
|
pwszParentCAName,
|
|
(DWORD const *) &disposition);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiInitializeCertSrvSecurity(
|
|
IN WCHAR const *pwszSanitizedCAName,
|
|
BOOL fUseEnterpriseACL, // which ACL to use
|
|
BOOL fSetDsSecurity) // whether to set security on DS object
|
|
{
|
|
HRESULT hr;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
CCertificateAuthoritySD CASD;
|
|
|
|
hr = CASD.InitializeFromTemplate(
|
|
fUseEnterpriseACL?
|
|
WSZ_DEFAULT_CA_ENT_SECURITY :
|
|
WSZ_DEFAULT_CA_STD_SECURITY,
|
|
pwszSanitizedCAName);
|
|
_JumpIfError(hr, error, "CProtectedSecurityDescriptor::InitializeFromTemplate");
|
|
|
|
hr = CASD.Save();
|
|
_JumpIfError(hr, error, "CProtectedSecurityDescriptor::Save");
|
|
|
|
hr = CASD.MapAndSetDaclOnObjects(fSetDsSecurity?true:false);
|
|
_JumpIfError(hr, error, "CProtectedSecurityDescriptor::MapAndSetDaclOnObjects");
|
|
|
|
error:
|
|
if (pSD)
|
|
{
|
|
LocalFree(pSD);
|
|
}
|
|
CSILOG(hr, IDS_ILOG_SETSECURITY, NULL, NULL, NULL);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiGenerateKeysOnly(
|
|
IN WCHAR const *pwszContainer,
|
|
IN WCHAR const *pwszProvName,
|
|
IN DWORD dwProvType,
|
|
IN BOOL fMachineKeyset,
|
|
IN DWORD dwKeyLength,
|
|
IN BOOL fUnattended,
|
|
IN BOOL fEnableKeyCounting,
|
|
OUT HCRYPTPROV *phProv,
|
|
OUT int *piMsg)
|
|
{
|
|
HRESULT hr;
|
|
HCRYPTKEY hKey = NULL;
|
|
DWORD dwKeyGenFlags;
|
|
DWORD dwAcquireFlags;
|
|
BOOL fExists;
|
|
|
|
*phProv = NULL;
|
|
*piMsg = 0;
|
|
|
|
// see if the container already exists
|
|
|
|
dwAcquireFlags = 0;
|
|
if (fMachineKeyset)
|
|
{
|
|
dwAcquireFlags |= CRYPT_MACHINE_KEYSET;
|
|
}
|
|
if (fUnattended)
|
|
{
|
|
dwAcquireFlags |= CRYPT_SILENT;
|
|
}
|
|
fExists = CryptAcquireContext(
|
|
phProv,
|
|
pwszContainer,
|
|
pwszProvName,
|
|
dwProvType,
|
|
CRYPT_SILENT | dwAcquireFlags);
|
|
if (NULL != *phProv)
|
|
{
|
|
CryptReleaseContext(*phProv, 0);
|
|
*phProv = NULL;
|
|
}
|
|
if (fExists)
|
|
{
|
|
// container exists, but we did not choose to reuse keys,
|
|
// so remove old keys and generate new ones.
|
|
|
|
if (!CryptAcquireContext(
|
|
phProv,
|
|
pwszContainer,
|
|
pwszProvName,
|
|
dwProvType,
|
|
CRYPT_DELETEKEYSET | dwAcquireFlags))
|
|
{
|
|
hr = myHLastError();
|
|
*piMsg = IDS_ERR_DELETEKEY;
|
|
_JumpError(hr, error, "CryptAcquireContext");
|
|
}
|
|
}
|
|
|
|
// create new container
|
|
|
|
if (!CryptAcquireContext(
|
|
phProv,
|
|
pwszContainer,
|
|
pwszProvName,
|
|
dwProvType,
|
|
CRYPT_NEWKEYSET | dwAcquireFlags)) // force new container
|
|
{
|
|
hr = myHLastError();
|
|
|
|
if (NTE_TOKEN_KEYSET_STORAGE_FULL == hr)
|
|
{
|
|
// Smart cards can only hold a limited number of keys
|
|
// The user must pick an existing key or use a blank card
|
|
|
|
*piMsg = IDS_ERR_FULL_TOKEN;
|
|
_JumpError(hr, error, "CryptAcquireContext");
|
|
}
|
|
else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
|
|
{
|
|
// user must have clicked cancel on a smart card dialog.
|
|
// go to previous page, and display no error message
|
|
|
|
_JumpError(hr, error, "CryptAcquireContext");
|
|
}
|
|
else if (NTE_EXISTS == hr)
|
|
{
|
|
// since fExists shows NOT, it is likely the current user
|
|
// doesn't have access permission for this existing key
|
|
*piMsg = IDS_ERR_NO_KEY_ACCESS;
|
|
_JumpError(hr, error, "CryptAcquireContext");
|
|
}
|
|
else
|
|
{
|
|
// Unexpected error in CryptAcquireContext.
|
|
*piMsg = IDS_ERR_BADCSP;
|
|
_JumpError(hr, error, "CryptAcquireContext");
|
|
}
|
|
}
|
|
|
|
// enable key usage count for audit purposes
|
|
|
|
if (fEnableKeyCounting)
|
|
{
|
|
hr = mySetEnablePrivateKeyUsageCount(*phProv, TRUE);
|
|
_JumpIfError(hr, error, "mySetEnablePrivateKeyUsageCount");
|
|
}
|
|
|
|
// set key length
|
|
|
|
dwKeyGenFlags = (dwKeyLength << 16) | CRYPT_EXPORTABLE;
|
|
|
|
// create signature keys
|
|
|
|
if (!CryptGenKey(*phProv, AT_SIGNATURE, dwKeyGenFlags, &hKey))
|
|
{
|
|
hr = myHLastError();
|
|
_PrintError(hr, "CryptGenKey(exportable)");
|
|
|
|
dwKeyGenFlags &= ~CRYPT_EXPORTABLE;
|
|
if (!CryptGenKey(*phProv, AT_SIGNATURE, dwKeyGenFlags, &hKey))
|
|
{
|
|
hr = myHLastError();
|
|
*piMsg = IDS_ERR_GENKEYFAIL;
|
|
_JumpError(hr, error, "CryptGenKey");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != hKey)
|
|
{
|
|
CryptDestroyKey(hKey);
|
|
}
|
|
CSILOG(hr, IDS_ILOG_GENERATEKEYS, pwszContainer, pwszProvName, &dwKeyLength);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiGenerateCAKeys(
|
|
IN WCHAR const *pwszContainer,
|
|
IN WCHAR const *pwszProvName,
|
|
IN DWORD dwProvType,
|
|
IN BOOL fMachineKeyset,
|
|
IN DWORD dwKeyLength,
|
|
IN HINSTANCE hInstance,
|
|
IN BOOL fUnattended,
|
|
IN BOOL fEnableKeyCounting,
|
|
IN HWND hwnd,
|
|
OUT BOOL *pfKeyGenFailed)
|
|
{
|
|
HRESULT hr;
|
|
HCRYPTPROV hProv = NULL;
|
|
int iMsg;
|
|
|
|
// generate key first
|
|
hr = csiGenerateKeysOnly(
|
|
pwszContainer,
|
|
pwszProvName,
|
|
dwProvType,
|
|
fMachineKeyset,
|
|
dwKeyLength,
|
|
fUnattended,
|
|
fEnableKeyCounting,
|
|
&hProv,
|
|
&iMsg);
|
|
if (S_OK != hr)
|
|
{
|
|
CertWarningMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
iMsg,
|
|
hr,
|
|
pwszContainer);
|
|
*pfKeyGenFailed = TRUE;
|
|
_JumpError(hr, error, "csiGenerateKeysOnly");
|
|
}
|
|
*pfKeyGenFailed = FALSE;
|
|
|
|
// now apply acl on key
|
|
// BUG, this is not necessary, CertSrvSetSecurity will reset
|
|
hr = csiSetKeyContainerSecurity(hProv);
|
|
if (S_OK != hr)
|
|
{
|
|
CertWarningMessageBox(
|
|
hInstance,
|
|
fUnattended,
|
|
hwnd,
|
|
IDS_ERR_KEYSECURITY,
|
|
hr,
|
|
pwszContainer);
|
|
_PrintError(hr, "csiSetKeyContainerSecurity");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != hProv)
|
|
{
|
|
CryptReleaseContext(hProv, 0);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
csiGetProviderTypeFromProviderName(
|
|
IN WCHAR const *pwszName,
|
|
OUT DWORD *pdwType)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
DWORD dwProvType;
|
|
WCHAR *pwszProvName = NULL;
|
|
|
|
// Provider name should not be null. This could be changed to ...
|
|
CSASSERT(NULL != pwszName);
|
|
|
|
*pdwType = 0;
|
|
dwProvType = 0;
|
|
|
|
for (i = 0; ; i++)
|
|
{
|
|
// get provider name
|
|
|
|
hr = myEnumProviders(i, NULL, 0, &dwProvType, &pwszProvName);
|
|
if (S_OK != hr)
|
|
{
|
|
hr = myHLastError();
|
|
CSASSERT(
|
|
HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr ||
|
|
NTE_FAIL == hr);
|
|
|
|
if(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr ||
|
|
NTE_FAIL == hr)
|
|
{
|
|
// no more providers, terminate loop
|
|
|
|
hr = E_INVALIDARG;
|
|
CSILOG(
|
|
hr,
|
|
IDS_ILOG_MISSING_PROVIDER,
|
|
pwszName,
|
|
NULL,
|
|
NULL);
|
|
_JumpErrorStr(hr, error, "not found", pwszName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CSASSERT(NULL != pwszProvName);
|
|
if (0 == mylstrcmpiL(pwszName, pwszProvName))
|
|
{
|
|
break; // found it
|
|
}
|
|
LocalFree(pwszProvName);
|
|
pwszProvName = NULL;
|
|
}
|
|
}
|
|
*pdwType = dwProvType;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszProvName)
|
|
{
|
|
LocalFree(pwszProvName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT
|
|
csiUpgradeCertSrvSecurity(
|
|
IN WCHAR const *pwszSanitizedCAName,
|
|
BOOL fUseEnterpriseACL, // which ACL to use
|
|
BOOL fSetDsSecurity, // whether to set security on DS object
|
|
CS_ENUM_UPGRADE UpgradeType)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CertSrv::CCertificateAuthoritySD CASD;
|
|
PSECURITY_DESCRIPTOR pDefaultSD = NULL;
|
|
|
|
hr = CASD.Initialize(pwszSanitizedCAName);
|
|
_PrintIfError(hr, "CASD.Initialize");
|
|
if(S_OK==hr)
|
|
{
|
|
if(CS_UPGRADE_WHISTLER==UpgradeType)
|
|
{
|
|
// validate the SD
|
|
hr = CASD.Validate(CASD.Get());
|
|
_PrintIfError(hr, "CASD.Validate");
|
|
}
|
|
else // win2k
|
|
{
|
|
hr = CASD.UpgradeWin2k(fUseEnterpriseACL?true:false);
|
|
_PrintIfError(hr, "CASD.UpgradeWin2k");
|
|
}
|
|
}
|
|
|
|
// never fail, fall back to a default SD
|
|
|
|
if(S_OK!=hr)
|
|
{
|
|
CASD.Uninitialize();
|
|
|
|
hr = CASD.InitializeFromTemplate(
|
|
fUseEnterpriseACL?
|
|
WSZ_DEFAULT_CA_ENT_SECURITY :
|
|
WSZ_DEFAULT_CA_STD_SECURITY,
|
|
pwszSanitizedCAName);
|
|
_JumpIfError(hr, error,
|
|
"CProtectedSecurityDescriptor::InitializeFromTemplate");
|
|
}
|
|
|
|
hr = CASD.Save();
|
|
_JumpIfError(hr, error, "CASD.Save");
|
|
|
|
hr = CASD.MapAndSetDaclOnObjects(fSetDsSecurity? true:false);
|
|
_PrintIfError(hr, "CASD::MapAndSetDaclOnObjects");
|
|
|
|
// When upgrading from win2k we need to upgrade security in DS. Usually
|
|
// DS is unavailable during upgrade.
|
|
if ((hr != S_OK) && fSetDsSecurity && (UpgradeType == CS_UPGRADE_WIN2000))
|
|
{
|
|
// if asked to set security on DS and this is UPGRADE, we can't touch DS.
|
|
// Leave security changes to the certsrv snapin
|
|
|
|
// set a flag so certmmc knows to do something
|
|
hr = SetSetupStatus(pwszSanitizedCAName, SETUP_W2K_SECURITY_NOT_UPGRADED_FLAG, TRUE);
|
|
_JumpIfError(hr, error, "SetSetupStatus");
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_CAN_NOT_COMPLETE);
|
|
_PrintError(hr, "DS unavailable");
|
|
}
|
|
else
|
|
{
|
|
// make sure this bit is cleared
|
|
hr = SetSetupStatus(pwszSanitizedCAName, SETUP_W2K_SECURITY_NOT_UPGRADED_FLAG, FALSE);
|
|
_JumpIfError(hr, error, "SetSetupStatus");
|
|
}
|
|
|
|
error:
|
|
LOCAL_FREE(pDefaultSD);
|
|
return hr;
|
|
}
|
|
|
|
DefineAutoClass(CAutoPCERT_NAME_INFO, PCERT_NAME_INFO, LocalFree);
|
|
DefineAutoClass(CAutoPCERT_RDN, PCERT_RDN, LocalFree);
|
|
|
|
HRESULT AddCNAndEncode(
|
|
LPCWSTR pcwszName,
|
|
LPCWSTR pcwszDNSuffix,
|
|
BYTE** ppbEncodedDN,
|
|
DWORD *pcbEncodedDN)
|
|
{
|
|
HRESULT hr;
|
|
CAutoPBYTE pbDNSuffix;
|
|
CAutoPCERT_NAME_INFO pCertNameInfo;
|
|
DWORD cbCertNameInfo;
|
|
CAutoPCERT_RDN pCertRDN;
|
|
CERT_RDN_ATTR attrDN;
|
|
CAutoPBYTE pbEncodedDN;
|
|
DWORD cbEncodedDN;
|
|
|
|
attrDN.pszObjId = szOID_COMMON_NAME;
|
|
attrDN.dwValueType = CERT_RDN_ANY_TYPE;
|
|
attrDN.Value.cbData = 0;
|
|
attrDN.Value.pbData = (PBYTE)pcwszName;
|
|
|
|
hr = myCertStrToName(
|
|
X509_ASN_ENCODING,
|
|
pcwszDNSuffix,
|
|
CERT_X500_NAME_STR |
|
|
CERT_NAME_STR_COMMA_FLAG |
|
|
CERT_NAME_STR_REVERSE_FLAG |
|
|
((g_dwNameEncodeFlags&CERT_RDN_ENABLE_UTF8_UNICODE_FLAG)?
|
|
CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG:0),
|
|
NULL,
|
|
&pbEncodedDN,
|
|
&cbEncodedDN,
|
|
NULL);
|
|
_JumpIfError(hr, error, "myCertStrToName");
|
|
|
|
if (!myDecodeName(
|
|
X509_ASN_ENCODING,
|
|
X509_UNICODE_NAME,
|
|
pbEncodedDN,
|
|
cbEncodedDN,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pCertNameInfo,
|
|
&cbCertNameInfo))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeName");
|
|
}
|
|
|
|
pCertRDN = (PCERT_RDN)LocalAlloc(
|
|
LMEM_FIXED,
|
|
(pCertNameInfo->cRDN+1)*sizeof(CERT_RDN));
|
|
_JumpIfAllocFailed(pCertRDN, error);
|
|
|
|
CopyMemory(
|
|
pCertRDN,
|
|
pCertNameInfo->rgRDN,
|
|
pCertNameInfo->cRDN*sizeof(CERT_RDN));
|
|
|
|
pCertRDN[pCertNameInfo->cRDN].cRDNAttr = 1;
|
|
pCertRDN[pCertNameInfo->cRDN].rgRDNAttr = &attrDN;
|
|
|
|
pCertNameInfo->cRDN++;
|
|
pCertNameInfo->rgRDN = pCertRDN;
|
|
|
|
if (!myEncodeName(
|
|
X509_ASN_ENCODING,
|
|
pCertNameInfo,
|
|
g_dwNameEncodeFlags,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
ppbEncodedDN,
|
|
pcbEncodedDN))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myEncodeName");
|
|
}
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myLDAPAddOrRemoveMachine(
|
|
bool fAdd,
|
|
LPWSTR pwszCertPublishersFilter)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPWSTR pwszComputerDN = NULL;
|
|
LDAP *pld = NULL;
|
|
LPWSTR pwszComputerDomainDN; // no free
|
|
LDAPMessage* pResult = NULL;
|
|
LDAPMessage *pEntry;
|
|
LPWSTR pwszAttrArray[2];
|
|
LPWSTR pwszDNAttr = L"distinguishedName";
|
|
LPWSTR pwszMemberAttr = L"member";
|
|
LPWSTR* pwszCertPublishersDN = NULL;
|
|
LDAPMod *mods[2];
|
|
LDAPMod member;
|
|
LPWSTR memberVals[2];
|
|
|
|
hr = myGetComputerObjectName(
|
|
NameFullyQualifiedDN,
|
|
&pwszComputerDN);
|
|
_JumpIfError(hr, error, "myGetComputerObjectName");
|
|
|
|
pwszComputerDomainDN = wcsstr(pwszComputerDN, L"DC=");
|
|
pwszAttrArray[0] = pwszDNAttr;
|
|
pwszAttrArray[1] = NULL;
|
|
|
|
hr = myLdapOpen(
|
|
NULL, // pwszDomainName
|
|
RLBF_REQUIRE_SECURE_LDAP, // dwFlags
|
|
&pld,
|
|
NULL, // pstrDomainDN
|
|
NULL); // pstrConfigDN
|
|
_JumpIfError(hr, error, "myLdapOpen");
|
|
|
|
hr = ldap_search_s(
|
|
pld,
|
|
pwszComputerDomainDN,
|
|
LDAP_SCOPE_SUBTREE,
|
|
pwszCertPublishersFilter,
|
|
pwszAttrArray,
|
|
FALSE,
|
|
&pResult);
|
|
hr = myHLdapError(pld, hr, NULL);
|
|
_JumpIfError(hr, error, "ldap_search_sW");
|
|
|
|
pEntry = ldap_first_entry(pld, pResult);
|
|
if (NULL == pEntry)
|
|
{
|
|
hr = ERROR_NO_SUCH_GROUP;
|
|
_JumpErrorStr(hr, error, "ldap_search", pwszCertPublishersFilter);
|
|
}
|
|
|
|
pwszCertPublishersDN = ldap_get_values(
|
|
pld,
|
|
pEntry,
|
|
pwszDNAttr);
|
|
|
|
if (NULL == pwszCertPublishersDN || NULL==*pwszCertPublishersDN)
|
|
{
|
|
hr = myHLdapLastError(pld, NULL);
|
|
_JumpError(hr, error, "ldap_get_values");
|
|
}
|
|
|
|
memberVals[0] = pwszComputerDN;
|
|
memberVals[1] = NULL;
|
|
member.mod_op = fAdd?LDAP_MOD_ADD:LDAP_MOD_DELETE;
|
|
member.mod_type = pwszMemberAttr;
|
|
member.mod_values = memberVals;
|
|
mods[0] = &member;
|
|
mods[1] = NULL;
|
|
|
|
hr = ldap_modify_ext_s(
|
|
pld,
|
|
*pwszCertPublishersDN,
|
|
mods,
|
|
NULL,
|
|
NULL);
|
|
// don't fail if already member of cert publishers
|
|
if(((HRESULT)LDAP_ALREADY_EXISTS)==hr)
|
|
hr = LDAP_SUCCESS;
|
|
|
|
hr = myHLdapError(pld, hr, NULL);
|
|
_JumpIfErrorStr(hr, error, "ldap_modify_exts", *pwszCertPublishersDN);
|
|
|
|
error:
|
|
LOCAL_FREE(pwszComputerDN);
|
|
if (NULL != pwszCertPublishersDN)
|
|
{
|
|
ldap_value_free(pwszCertPublishersDN);
|
|
}
|
|
if (NULL != pResult)
|
|
{
|
|
ldap_msgfree(pResult);
|
|
}
|
|
myLdapClose(pld, NULL, NULL);
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT AddOrRemoveMachineToGroup(
|
|
PSID pGroupSid,
|
|
bool fAdd)
|
|
{
|
|
HRESULT hr;
|
|
LPWSTR pwszFilter = NULL;
|
|
LPWSTR pwszEscapedGroupSid = NULL;
|
|
ULONG len;
|
|
|
|
static LPCWSTR pcwszFilterFormat =
|
|
L"(&(objectCategory=group)(objectSid=%s))";
|
|
|
|
len = ldap_escape_filter_element(
|
|
(PCHAR)pGroupSid,
|
|
GetLengthSid(pGroupSid),
|
|
NULL,
|
|
0);
|
|
|
|
len *= sizeof(WCHAR);
|
|
|
|
pwszEscapedGroupSid = (LPWSTR) LocalAlloc(LMEM_FIXED, len);
|
|
_JumpIfAllocFailed(pwszEscapedGroupSid, error);
|
|
|
|
ldap_escape_filter_element(
|
|
(PCHAR)pGroupSid,
|
|
GetLengthSid(pGroupSid),
|
|
pwszEscapedGroupSid,
|
|
len);
|
|
|
|
pwszFilter = (LPWSTR)LocalAlloc(
|
|
LMEM_FIXED,
|
|
sizeof(WCHAR) *
|
|
(wcslen(pcwszFilterFormat) +
|
|
wcslen(pwszEscapedGroupSid) +
|
|
1));
|
|
_JumpIfAllocFailed(pwszFilter, error);
|
|
|
|
wsprintf(
|
|
pwszFilter,
|
|
pcwszFilterFormat,
|
|
pwszEscapedGroupSid);
|
|
|
|
hr = myLDAPAddOrRemoveMachine(fAdd, pwszFilter);
|
|
_JumpIfError(hr, error, "myLDAPAddOrRemoveMachine");
|
|
|
|
error:
|
|
LOCAL_FREE(pwszFilter);
|
|
LOCAL_FREE(pwszEscapedGroupSid);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT AddOrRemoveMachineToGroup(
|
|
LPCWSTR pcwszGroupSid,
|
|
bool fAdd)
|
|
{
|
|
HRESULT hr;
|
|
PSID pGroupSid = NULL;
|
|
|
|
if(!ConvertStringSidToSid(
|
|
pcwszGroupSid,
|
|
&pGroupSid))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpErrorStr(hr, error, "ConvertStringSidToSid", pcwszGroupSid);
|
|
}
|
|
myRegisterMemAlloc(pGroupSid, -1, CSM_LOCALALLOC);
|
|
|
|
hr = AddOrRemoveMachineToGroup(
|
|
pGroupSid,
|
|
fAdd);
|
|
_JumpIfError(hr, error, "AddOrRemoveMachineToGroup");
|
|
|
|
error:
|
|
LOCAL_FREE(pGroupSid);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
AddOrRemoveMachineToDomainGroup(
|
|
IN bool fAdd)
|
|
{
|
|
HRESULT hr;
|
|
PSID pGroupSid = NULL;
|
|
|
|
hr = myGetSidFromRid(
|
|
DOMAIN_GROUP_RID_CERT_ADMINS,
|
|
&pGroupSid,
|
|
NULL);
|
|
_JumpIfError(hr, error, "myGetSidFromRid");
|
|
|
|
hr = AddOrRemoveMachineToGroup(pGroupSid, fAdd);
|
|
_JumpIfError(hr, error, "AddOrRemoveMachineToGroup");
|
|
|
|
error:
|
|
LOCAL_FREE(pGroupSid);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
AddCAMachineToCertPublishers()
|
|
{
|
|
return AddOrRemoveMachineToDomainGroup(true);
|
|
}
|
|
|
|
HRESULT
|
|
RemoveCAMachineFromCertPublishers(VOID)
|
|
{
|
|
return AddOrRemoveMachineToDomainGroup(false);
|
|
}
|
|
|
|
static LPCWSTR pcwszPreWin2kSid = L"S-1-5-32-554";
|
|
|
|
HRESULT AddCAMachineToPreWin2kGroup(VOID)
|
|
{
|
|
return AddOrRemoveMachineToGroup(
|
|
pcwszPreWin2kSid,
|
|
true);
|
|
}
|
|
|
|
HRESULT RemoveCAMachineFromPreWin2kGroup(VOID)
|
|
{
|
|
return AddOrRemoveMachineToGroup(
|
|
pcwszPreWin2kSid,
|
|
false);
|
|
}
|