//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1995 - 1999 // // File: initlib.cpp // // Contents: Install cert server // //-------------------------------------------------------------------------- #include #pragma hdrstop // C Run-Time Includes #include #include #include #include #include #include #include #include #include // Windows System Includes #include #include #include #include #include #include #include #include #include #include #include #define SECURITY_WIN32 #include #include // 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, // // // 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= // CN=CA foo // CN=CA foo(1) // CN=CA foo(3) // CN= // 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; do { // 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); break; } if (!WriteFile(hLocalFile, pbDER, cbDER, &dwBytesWritten, NULL)) { hr = myHLastError(); CertErrorMessageBox( hInstance, fUnattended, hwnd, IDS_ERR_WRITEFILE, hr, pwszFileName); break; } fResult = TRUE; } while (FALSE); 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; if (!myEncodeKeyUsage( X509_ASN_ENCODING, &KeyUsage, CERTLIB_USE_LOCALALLOC, &pbEncoded, &cbEncoded)) { hr = myHLastError(); CertErrorMessageBox( hInstance, fUnattended, hwnd, IDS_ERR_ENCODEKEYATTR, hr, NULL); 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, IN BOOL fUseDS, 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"); if (fUseDS) { // bind to ds hr = myRobustLdapBind(&pld, FALSE); _JumpIfError(hr, error, "myRobustLdapBind"); hr = myGetAuthoritativeDomainDn(pld, &strDomainDN, &strConfigDN); if (S_OK != hr) { hr = HRESULT_FROM_WIN32(hr); _JumpError(hr, error, "myGetAuthoritativeDomainDn"); } } else { 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 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); } if (NULL != pld) { ldap_unbind(pld); } if (NULL != strConfigDN) { SysFreeString(strConfigDN); } if (NULL != strDomainDN) { SysFreeString(strDomainDN); } 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); } if (S_OK != hr) { if (S_FALSE == hr) { _JumpError2(hr, error, "myInfGetCRLDistributionPoints", hr); } hr = GetRegCRLDistributionPoints( pwszSanitizedName, fUseDS, &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); } _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(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; 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; LPCWSTR pszErrorPtr; 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); 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); _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); _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); _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); _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 // 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; 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 != 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 fBase, WCHAR** ppwszCRLPeriod, DWORD* pdwCRLCount) { HRESULT hr; HINF hInf = INVALID_HANDLE_VALUE; DWORD ErrorLine; hr = myInfOpenFile(NULL, &hInf, &ErrorLine); _JumpIfError2( hr, error, "myInfOpenFile", HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); if (INVALID_HANDLE_VALUE != hInf) { hr = myinfGetCRLPublicationParams( hInf, fBase? wszINFKEY_CRLPERIODSTRING : wszINFKEY_CRLDELTAPERIODSTRING, fBase? wszINFKEY_CRLPERIODCOUNT : wszINFKEY_CRLDELTAPERIODCOUNT, ppwszCRLPeriod, pdwCRLCount); _JumpIfError(hr, error, "myinfGetCRLPublicationParams"); } 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) { HRESULT hr; BOOL fCA; CERT_EXTENSION *pExt; DWORD cb; CERT_BASIC_CONSTRAINTS2_INFO Constraints; fCA = FALSE; pExt = CertFindExtension( szOID_BASIC_CONSTRAINTS2, pCert->pCertInfo->cExtension, pCert->pCertInfo->rgExtension); if (NULL == pExt) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _PrintError(hr, "CertFindExtension"); } 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(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 while (TRUE) { 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 }; HRESULT CreateCDPAndAIAAndKRAEntry( IN WCHAR const *pwszSanitizedCAName, IN WCHAR const *pwszSanitizedDSName, IN WCHAR const *pwszServerName, IN DWORD iCert, IN DWORD iCRL, IN BOOL fRenew, 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[3]; WCHAR *apwszOut[3]; DWORD i; DWORD dwDisp; WCHAR *pwszError = NULL; ZeroMemory(apwszOut, sizeof(apwszOut)); hr = myRobustLdapBind(&pld, FALSE); _JumpIfError(hr, error, "myRobustLdapBind"); hr = myGetAuthoritativeDomainDn(pld, &strDomainDN, &strConfigDN); if (S_OK != hr) { hr = myHError(hr); _JumpError(hr, error, "myGetAuthoritativeDomainDn"); } DBGPRINT((DBG_SS_CERTLIBI, "DomainDN='%ws'\n", strDomainDN)); DBGPRINT((DBG_SS_CERTLIBI, "ConfigDN='%ws'\n", strConfigDN)); apwszIn[0] = g_wszCDPDNTemplate; apwszIn[1] = g_wszAIADNTemplate; apwszIn[2] = g_wszKRADNTemplate; // Format the CDP and AIA templates into real names hr = myFormatCertsrvStringArray( FALSE, // fURL pwszServerName, // pwszServerName_p1_2 pwszSanitizedCAName, // pwszSanitizedName_p3_7 iCert, // iCert_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"); pwszCDPDN = apwszOut[0]; pwszAIADN = apwszOut[1]; pwszKRADN = apwszOut[2]; DBGPRINT((DBG_SS_CERTLIBI, "CDPDN='%ws'\n", pwszCDPDN)); DBGPRINT((DBG_SS_CERTLIBI, "AIADN='%ws'\n", pwszAIADN)); DBGPRINT((DBG_SS_CERTLIBI, "KRADN='%ws'\n", pwszKRADN)); //+===================================================================== // Create the CDP container and object: hr = myLdapCreateContainer( pld, pwszCDPDN, TRUE, 1, pContainerSD, &pwszError); _JumpIfError(hr, error, "myLdapCreateContainer"); CSASSERT(NULL == pwszError); hr = myLdapCreateCDPObject(pld, pwszCDPDN, pSD, &dwDisp, &pwszError); _JumpIfErrorStr(hr, error, "myLdapCreateCDPObject", pwszCDPDN); CSASSERT(NULL == pwszError); //+===================================================================== // 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); 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 != pwszError) { LocalFree(pwszError); } for (i = 0; i < ARRAYSIZE(apwszOut); i++) { if (NULL != apwszOut[i]) { LocalFree(apwszOut[i]); } } if (NULL != strConfigDN) { SysFreeString(strConfigDN); } if (NULL != strDomainDN) { SysFreeString(strDomainDN); } if (NULL != pld) { ldap_unbind(pld); } 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 = myRobustLdapBind(&pld, FALSE); _JumpIfError(hr, error, "myRobustLdapBind"); hr = myGetAuthoritativeDomainDn(pld, &strDomainDN, &strConfig); if (S_OK != hr) { hr = myHError(hr); _JumpError(hr, error, "myGetAuthoritativeDomainDn"); } 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, &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); } if (NULL != strConfig) { SysFreeString(strConfig); } if (NULL != strDomainDN) { SysFreeString(strDomainDN); } if (NULL != pld) { ldap_unbind(pld); } 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_ADDSYSTEM32DIR, wszCRLPATHDEFAULT, }, { CSURL_SERVERPUBLISH | CSURL_ADDTOCERTCDP | CSURL_ADDTOFRESHESTCRL | CSURL_ADDTOCRLCDP | CSURL_DSONLY, const_cast(g_wszzLDAPRevocationURLTemplate), }, { CSURL_ADDTOCERTCDP | CSURL_ADDTOFRESHESTCRL | CSURL_ADDTOCRLCDP, const_cast(g_wszHTTPRevocationURLTemplate), }, { CSURL_ADDTOCERTCDP | CSURL_ADDTOFRESHESTCRL | CSURL_ADDTOCRLCDP | CSURL_NODS, const_cast(g_wszFILERevocationURLTemplate), }, #if 0 { CSURL_SERVERPUBLISH | CSURL_DSONLY, const_cast(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(g_wszzLDAPIssuerCertURLTemplate), }, { CSURL_ADDTOCERTCDP, const_cast(g_wszHTTPIssuerCertURLTemplate), }, { CSURL_ADDTOCERTCDP | CSURL_NODS, const_cast(g_wszFILEIssuerCertURLTemplate), }, #if 0 { CSURL_SERVERPUBLISH | CSURL_DSONLY, const_cast(g_wszAIADNTemplate), }, #endif { 0, NULL } }; #define CSURL_DSDEPENDENT (CSURL_SERVERPUBLISH | 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[22]; 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 HWND hwnd, IN WCHAR const *pwszCAServer, IN WCHAR const *pwszSanitizedCAName, IN WCHAR const *pwszCADisplayName, IN WCHAR const *pwszCADescription, 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; WCHAR wszNameBuffer[MAX_COMPUTER_DNS_NAME + 1]; DWORD cNameBuffer; 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. cNameBuffer = ARRAYSIZE(wszNameBuffer); if (!GetComputerObjectName(NameSamCompatible, wszNameBuffer, &cNameBuffer)) { hr = myHLastError(); _JumpError(hr, error, "GetComputerObjectName"); } 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, pwszSanitizedDSName, pwszCAServer, iCert, iCRL, fRenew, 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(pwszCAServer); pCAProp[1] = NULL; hr = CASetCAProperty(hCAInfo, CA_PROP_DNSNAME, pCAProp); _JumpIfError(hr, error, "CASetCAProperty(CA_PROP_DNSNAME)"); pCAProp[0] = const_cast(pwszCADisplayName); pCAProp[1] = NULL; hr = CASetCAProperty(hCAInfo, CA_PROP_DISPLAY_NAME, pCAProp); _JumpIfError(hr, error, "CASetCAProperty(CA_PROP_DISPLAY_NAME)"); if (NULL != pwszCADescription) { pCAProp[0] = const_cast(pwszCADescription); pCAProp[1] = NULL; hr = CASetCAProperty(hCAInfo, CA_PROP_DESCRIPTION, pCAProp); _JumpIfError(hr, error, "CASetCAProperty(CA_PROP_DESCRIPTION)"); } 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, FIsAdvancedServer()? s_apwszCertTypeAdvancedServer : s_apwszCertTypeServer); _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; PSECURITY_DESCRIPTOR pSD = NULL; hr = myGetSDFromTemplate(WSZ_DEFAULT_KEYCONTAINER_SECURITY, NULL, &pSD); _JumpIfError(hr, error, "myGetSDFromTemplate"); // apply the security descriptor to the key container if (!CryptSetProvParam( hProv, PP_KEYSET_SEC_DESCR, (BYTE *) pSD, (DWORD) DACL_SECURITY_INFORMATION)) { hr = myHLastError(); _JumpError(hr, error, "CryptSetProvParam"); } hr = S_OK; error: if (NULL != pSD) { LocalFree(pSD); } 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; DWORD i; 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(ENUM_ENTERPRISE_ROOTCA==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 AddCertBlobToStore( IN OUT HCERTSTORE hStore, IN BYTE const *pb, IN DWORD cb) { HRESULT hr; CERT_CONTEXT const *pCert = NULL; 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"); } hr = S_OK; error: if (NULL != pCert) { CertFreeCertificateContext(pCert); } 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 = AddCertBlobToStore(hStore, pb, cb); _JumpIfError(hr, error, "AddCertBlobToStore"); 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 = AddCertBlobToStore( hStore, pSeq->rgValue[iCert].pbData, pSeq->rgValue[iCert].cbData); _JumpIfError(hr, error, "AddCertBlobToStore"); } } } if (fTryPKCS7) { CRYPT_DATA_BLOB blobPKCS7; blobPKCS7.pbData = const_cast(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"); } while (TRUE) { pCert = CertEnumCertificatesInStore(hStorePKCS7, pCert); if (NULL == pCert) { break; } if (!CertAddCertificateContextToStore( hStore, pCert, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) { hr = myHLastError(); _JumpError(hr, error, "CertAddCertificateContextToStore"); } } } 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 while (TRUE) { 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 BuildCAChainFromCert( IN HINSTANCE hInstance, IN BOOL fUnattended, IN HWND hwnd, IN CRYPT_KEY_PROV_INFO const *pKeyProvInfo, IN WCHAR const *pwszCommonName, IN const ENUM_CATYPES CAType, OPTIONAL IN BYTE const *pbChain, IN DWORD cbChain, IN CERT_CONTEXT const *pCert) { HRESULT hr; CERT_NAME_INFO *pNameInfo = NULL; DWORD cbNameInfo; WCHAR const *pwszCN; // 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(pNameInfo, szOID_COMMON_NAME, &pwszCN); _PrintIfError(hr, "myGetCertNameProperty"); if (S_OK == hr && 0 != lstrcmp(pwszCommonName, pwszCN)) { hr = E_INVALIDARG; } if (S_OK != hr) { CertErrorMessageBox( hInstance, fUnattended, hwnd, IDS_ERR_NOT_MATCH_NAME, hr, NULL); _JumpError(hr, error, "common name in cert not match"); } hr = IsCACert(hInstance, fUnattended, hwnd, pCert); _JumpIfError(hr, error, "IsCACert"); hr = InstallCAChain( hInstance, fUnattended, hwnd, pCert, pKeyProvInfo, CAType, pbChain, cbChain); _JumpIfError(hr, error, "InstallCAChain"); error: 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; 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, pwszCACommonName, CAType, pbChainOrCert, cbChainOrCert, pCert); _JumpIfError(hr, error, "BuildCAChainFromCert"); if (fUseDS) { // save in ds hr = csiSetupCAInDS( hwnd, pwszServerName, pwszSanitizedCAName, pwszCACommonName, NULL, // pwszCADescription CAType, iCert, iCRL, fRenew, pCert); _JumpIfError(hr, error, "csiSetupCAInDS"); } // store CA cert hash hr = mySetCARegHash(pwszSanitizedCAName, CSRH_CASIGCERT, iCert, pCert); _JumpIfError(hr, error, "mySetCARegHash"); if (NULL != pwszCACertFile) { // write this CA cert into shared folder if (!DeleteFile(pwszCACertFile)) { hr = myHLastError(); if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != hr) { _JumpError(hr, error, "DeleteFile"); } } if (!csiWriteDERToFile( pwszCACertFile, (BYTE *) pCert->pbCertEncoded, pCert->cbCertEncoded, hInstance, fUnattended, hwnd)) { hr = myHLastError(); _JumpError(hr, error, "csiWriteDERToFile"); } } // write cert file for web pages if (0 == GetEnvironmentVariable(L"SystemRoot", wszTemp, MAX_PATH)) { 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)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, &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 BOOL fUnattended, IN HWND hwnd, 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 ICertRequest *pICertRequest, IN LONG disposition, IN BSTR strDispositionMessage, IN BOOL fRenew, IN LONG requestId, IN HRESULT hrSubmit, IN HRESULT hrLastStatus, IN int iMsgId) { HRESULT hr; WCHAR *pwszHelpMsg = NULL; DWORD dwStatusDisable; DWORD dwStatusEnable; BOOL fPopup = FALSE; CSASSERT(NULL != pICertRequest); // form custom message hr = FormRequestHelpMessage( hInstance, fUnattended, hwnd, requestId, strDispositionMessage, pwszParentCAConfig, &pwszHelpMsg); _JumpIfError(hr, error, "FromRequestHelpMessage"); // Assume suspended install, denied request: dwStatusEnable = SETUP_DENIED_FLAG | SETUP_REQUEST_FLAG; if (!fRenew) { 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 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, pICertRequest, disposition, strDispositionMessage, fRenew, 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; DWORD cbLength; 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, 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"); } } // set key length dwKeyGenFlags = (dwKeyLength << 16) | CRYPT_EXPORTABLE; // create signature keys 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 HWND hwnd, OUT BOOL *pfKeyGenFailed) { HRESULT hr; HCRYPTPROV hProv = NULL; int iMsg; // generate key first hr = csiGenerateKeysOnly( pwszContainer, pwszProvName, dwProvType, fMachineKeyset, dwKeyLength, fUnattended, &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 == lstrcmpi(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"); if ((hr != S_OK) && fSetDsSecurity && (UpgradeType != CS_UPGRADE_NO)) { // 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; DWORD cbDNSuffix; 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; } /* * Add/remove computer account to the Cert Publishers group for the forest */ HRESULT AddOrRemoveCAMachineToCertPublishers(bool fAdd) { HRESULT hr = S_OK; LPWSTR pwszComputerDN = NULL; LDAP *pld = NULL; LPWSTR pcwszComputerDomainDN; // no free LPWSTR pcwszCertPublishersFilter = L"(&(objectClass=group)(CN=Cert Publishers))"; LDAPMessage* pResult = NULL; LDAPMessage *pEntry; LPWSTR pwszAttrArray[2]; BerElement *pber; 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"); pcwszComputerDomainDN = wcsstr(pwszComputerDN, L"DC="); pwszAttrArray[0] = pwszDNAttr; pwszAttrArray[1] = NULL; hr = myRobustLdapBind( &pld, FALSE); // no GC _JumpIfError(hr, error, "myRobustLdapBind"); hr = ldap_search_s( pld, pcwszComputerDomainDN, LDAP_SCOPE_SUBTREE, pcwszCertPublishersFilter, pwszAttrArray, FALSE, &pResult); hr = myHLdapError(pld, hr, NULL); _JumpIfError(hr, error, "ldap_search_sW"); pEntry = ldap_first_entry(pld, pResult); if (NULL == pEntry) { hr = myHLdapLastError(pld, NULL); _JumpError(hr, error, "ldap_first_entry"); } 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(pwszCertPublishersDN) { ldap_value_free(pwszCertPublishersDN); } if (NULL != pResult) { ldap_msgfree(pResult); } if (pld) ldap_unbind(pld); return hr; } HRESULT AddCAMachineToCertPublishers(VOID) { return AddOrRemoveCAMachineToCertPublishers(true); } HRESULT RemoveCAMachineFromCertPublishers(VOID) { return AddOrRemoveCAMachineToCertPublishers(false); }