//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: cainfoc.cpp // // Contents: CCAInfo implemenation // // History: 16-Dec-97 petesk created // //--------------------------------------------------------------------------- #include "pch.cpp" #pragma hdrstop #include "cainfoc.h" #include "certtype.h" #include "csldap.h" #include #include #include #include #include #include #include #define __dwFILE__ __dwFILE_CERTCLIB_CAINFOC_CPP__ #define wcLBRACE L'{' #define wcRBRACE L'}' LPCWSTR g_wszEnrollmentServiceLocation = L"CN=Enrollment Services,CN=Public Key Services,CN=Services,"; #define LDAP_SECURITY_DESCRIPTOR_NAME L"NTSecurityDescriptor" #define LDAP_CERTIFICATE_TEMPLATES_NAME L"certificateTemplates" WCHAR *g_awszCAAttrs[] = { CA_PROP_NAME, CA_PROP_DISPLAY_NAME, CA_PROP_FLAGS, CA_PROP_DNSNAME, CA_PROP_DSLOCATION, CA_PROP_CERT_DN, CA_PROP_CERT_TYPES, CA_PROP_SIGNATURE_ALGS, CA_PROP_DESCRIPTION, L"cACertificate", L"objectClass", LDAP_SECURITY_DESCRIPTOR_NAME, NULL}; WCHAR *g_awszCANamedProps[] = { CA_PROP_NAME, CA_PROP_DISPLAY_NAME, CA_PROP_DNSNAME, CA_PROP_DSLOCATION, CA_PROP_CERT_DN, CA_PROP_CERT_TYPES, CA_PROP_SIGNATURE_ALGS, CA_PROP_DESCRIPTION, NULL}; LPWSTR g_awszSignatureAlgs[] = { TEXT(szOID_RSA_MD2RSA), TEXT(szOID_RSA_MD4RSA), TEXT(szOID_RSA_MD5RSA), TEXT(szOID_RSA_SHA1RSA), NULL }; //+-------------------------------------------------------------------------- // CCAInfo::~CCAInfo -- destructor // // free memory associated with this instance //+-------------------------------------------------------------------------- CCAInfo::~CCAInfo() { _Cleanup(); } //+-------------------------------------------------------------------------- // CCAInfo::_Cleanup -- free memory // // free memory associated with this instance //+-------------------------------------------------------------------------- HRESULT CCAInfo::_Cleanup() { // Cleanup will only be called if there is no previous element // to reference this element, and the caller is also releaseing. // If there is a further element, release it. if (NULL != m_pNext) { m_pNext->Release(); m_pNext = NULL; } CCAProperty::DeleteChain(&m_pProperties); if (NULL != m_pCertificate) { CertFreeCertificateContext(m_pCertificate); m_pCertificate = NULL; } if (NULL != m_bstrDN) { CertFreeString(m_bstrDN); m_bstrDN = NULL; } if (NULL != m_pSD) { LocalFree(m_pSD); m_pSD = NULL; } return(S_OK); } //+-------------------------------------------------------------------------- // CCAInfo::_Cleanup -- add reference // // //+-------------------------------------------------------------------------- DWORD CCAInfo::AddRef() { return(InterlockedIncrement(&m_cRef)); } //+-------------------------------------------------------------------------- // CCAInfo::Release -- release reference // // //+-------------------------------------------------------------------------- DWORD CCAInfo::Release() { DWORD cRef; if (0 == (cRef = InterlockedDecrement(&m_cRef))) { delete this; } return(cRef); } //+-------------------------------------------------------------------------- // CCAInfo::Find -- Find CA Objects in the DS // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::Find( LPCWSTR wszQuery, LPCWSTR wszScope, DWORD dwFlags, CCAInfo **ppCAInfo) { HRESULT hr = S_OK; LDAP * pld = NULL; // Initialize LDAP session WCHAR * wszSearch = L"(objectCategory=pKIEnrollmentService)"; DWORD cSearchParam; CERTSTR bstrSearchParam = NULL; CERTSTR bstrScope = NULL; CERTSTR bstrConfig = NULL; CERTSTR bstrDomain = NULL; if (NULL == ppCAInfo) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } // short circuit calls to a nonexistant DS hr = myDoesDSExist(TRUE); _JumpIfError4( hr, error, "myDoesDSExist", HRESULT_FROM_WIN32(ERROR_SERVER_DISABLED), HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN), HRESULT_FROM_WIN32(ERROR_NETWORK_UNREACHABLE)); __try { if(CA_FLAG_SCOPE_IS_LDAP_HANDLE & dwFlags) { pld = (LDAP *)wszScope; } else { // bind to ds hr = myRobustLdapBindEx( 0, // dwFlags1 RLBF_REQUIRE_SECURE_LDAP, // dwFlags2 LDAP_VERSION2, // uVersion NULL, // pwszDomainName &pld, NULL); // ppwszForestDNSName _LeaveIfError2( hr, "myRobustLdapBindEx", HRESULT_FROM_WIN32(ERROR_WRONG_PASSWORD)); if(wszScope) { bstrScope = CertAllocString((LPWSTR)wszScope); if(bstrScope == NULL) { hr = E_OUTOFMEMORY; _LeaveError(hr, "CertAllocString"); } } } if(bstrScope == NULL) { // If scope is not specified, set it to the // current domain scope. hr = CAGetAuthoritativeDomainDn(pld, &bstrDomain, &bstrConfig); if(S_OK != hr) { _LeaveError(hr, "CAGetAuthoritativeDomainDn"); } bstrScope = CertAllocStringLen( NULL, wcslen(bstrConfig) + wcslen(g_wszEnrollmentServiceLocation)); if(bstrScope == NULL) { hr = E_OUTOFMEMORY; _LeaveError(hr, "CertAllocStringLen"); } wcscpy(bstrScope, g_wszEnrollmentServiceLocation); wcscat(bstrScope, bstrConfig); } if (NULL != wszQuery) { // If a query is specified, then combine it with the // objectCategory=pKIEnrollmentService query cSearchParam = 2 + wcslen(wszSearch) + wcslen(wszQuery) + 2; bstrSearchParam = CertAllocStringLen(NULL,cSearchParam); if(bstrSearchParam == NULL) { hr = E_OUTOFMEMORY; _LeaveError(hr, "CertAllocStringLen"); } wcscpy(bstrSearchParam, L"(&"); wcscat(bstrSearchParam, wszSearch); wcscat(bstrSearchParam, wszQuery); wcscat(bstrSearchParam, L")"); } hr = _ProcessFind(pld, (wszQuery? bstrSearchParam : wszSearch), bstrScope, dwFlags, ppCAInfo); if(hr != S_OK) { _LeaveError(hr, "_ProcessFind"); } } __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER) { } error: if (NULL != bstrScope) { CertFreeString(bstrScope); } if( NULL != bstrConfig) { CertFreeString(bstrConfig); } if( NULL != bstrDomain) { CertFreeString(bstrDomain); } if (NULL != bstrSearchParam) { CertFreeString(bstrSearchParam); } if(0 == (CA_FLAG_SCOPE_IS_LDAP_HANDLE & dwFlags)) { if (NULL != pld) { ldap_unbind(pld); } } return(hr); } //+-------------------------------------------------------------------------- // CCAInfo::_ProcessFind -- ProcessFind CA Objects in the DS // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::_ProcessFind( LDAP * pld, LPCWSTR wszQuery, LPCWSTR wszScope, DWORD dwFlags, CCAInfo **ppCAInfo) { HRESULT hr = S_OK; ULONG ldaperr; CCAInfo *pCAFirst = NULL; CCAInfo *pCACurrent = NULL; // Initialize LDAP session CHAR sdBerValue[] = {0x30, 0x03, 0x02, 0x01, DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION}; LDAPControl se_info_control = { LDAP_SERVER_SD_FLAGS_OID_W, { 5, sdBerValue }, TRUE }; PLDAPControl server_controls[2] = { &se_info_control, NULL }; LDAPMessage *SearchResult = NULL, *Entry; struct berval **apCerts; struct berval **apSD; PCCERT_CHAIN_CONTEXT pChainContext = NULL; PCCERT_CONTEXT pCert = NULL; DWORD cEntries; // search timeout struct l_timeval timeout; if (NULL == ppCAInfo) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } *ppCAInfo = NULL; DBGPRINT(( DBG_SS_CERTLIBI, "_ProcessFind(Query=%ws, Scope=%ws, Flags=%x)\n", wszQuery, wszScope, dwFlags)); timeout.tv_sec = csecLDAPTIMEOUT; timeout.tv_usec = 0; // Perform search. ldaperr = ldap_search_ext_sW(pld, (LPWSTR)wszScope, LDAP_SCOPE_SUBTREE, (LPWSTR)wszQuery, g_awszCAAttrs, 0, (PLDAPControl *) server_controls, NULL, &timeout, 10000, &SearchResult); if(ldaperr == LDAP_NO_SUCH_OBJECT) { // No entries were found. hr = S_OK; DBGPRINT((DBG_SS_CERTLIBI, "ldap_search_ext_sW: no entries\n")); goto error; } if(ldaperr != LDAP_SUCCESS) { hr = myHLdapError(pld, ldaperr, NULL); _JumpError(hr, error, "ldap_search_ext_sW"); } cEntries = ldap_count_entries(pld, SearchResult); DBGPRINT((DBG_SS_CERTLIBI, "ldap_count_entries: %u entries\n", cEntries)); if (0 == cEntries) { // No entries were found. hr = S_OK; goto error; } hr = S_OK; for(Entry = ldap_first_entry(pld, SearchResult); Entry != NULL; Entry = ldap_next_entry(pld, Entry)) { CCAProperty *pProp; WCHAR ** pwszProp; WCHAR ** wszLdapVal; DWORD dwCAFlags = 0; if(pCert) { CertFreeCertificateContext(pCert); pCert = NULL; } if(pChainContext) { CertFreeCertificateChain(pChainContext); pChainContext = NULL; } wszLdapVal = ldap_get_values(pld, Entry, CA_PROP_FLAGS); if(wszLdapVal != NULL) { if(wszLdapVal[0] != NULL) { dwCAFlags = wcstoul(wszLdapVal[0], NULL, 10); } ldap_value_free(wszLdapVal); } DBGPRINT((DBG_SS_CERTLIBI, "dwCAFlags=%x\n", dwCAFlags)); // Filter of flags if(( 0 == (dwFlags & CA_FIND_INCLUDE_NON_TEMPLATE_CA)) && ( 0 != (dwCAFlags & CA_FLAG_NO_TEMPLATE_SUPPORT))) { // Don't include standalone CA's unless instructed to DBGPRINT(( DBG_SS_CERTLIBI, "Skipping non-template CA, dwCAFlags=%x\n", dwCAFlags)); continue; } // Get the CA Certificate apCerts = ldap_get_values_len(pld, Entry, L"cACertificate"); if(apCerts && apCerts[0]) { pCert = CertCreateCertificateContext( X509_ASN_ENCODING, (PBYTE)apCerts[0]->bv_val, apCerts[0]->bv_len); ldap_value_free_len(apCerts); } if(0 == (CA_FIND_INCLUDE_UNTRUSTED & dwFlags)) { if (NULL == pCert) { DBGPRINT((DBG_SS_CERTLIBI, "Skipping cert-less CA\n")); continue; // skip this CA } // Verify cert and chain... hr = myVerifyCertContext( pCert, CA_VERIFY_FLAGS_IGNORE_OFFLINE, // dwFlags 0, // cUsageOids NULL, // apszUsageOids (dwFlags & CA_FIND_LOCAL_SYSTEM)? HCCE_LOCAL_MACHINE : HCCE_CURRENT_USER, NULL, // hAdditionalStore NULL); // ppwszMissingIssuer if (S_OK != hr) { HRESULT hr2; WCHAR *pwszSubject = NULL; hr2 = myCertNameToStr( X509_ASN_ENCODING, &pCert->pCertInfo->Subject, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, &pwszSubject); _PrintIfError(hr2, "myCertNameToStr"); _PrintErrorStr(hr, "myVerifyCertContext", pwszSubject); if (NULL != pwszSubject) { LocalFree(pwszSubject); } hr = S_OK; continue; // skip this CA } } // Is this the first one? if(pCACurrent) { pCACurrent->m_pNext = new CCAInfo; if(pCACurrent->m_pNext == NULL) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "new"); } pCACurrent = pCACurrent->m_pNext; } else { pCAFirst = pCACurrent = new CCAInfo; if(pCAFirst == NULL) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "new"); } } pCACurrent->m_pCertificate = pCert; pCert = NULL; WCHAR* wszDN = ldap_get_dnW(pld, Entry); if(NULL == wszDN) { hr = myHLdapLastError(pld, NULL); _JumpError(hr, error, "ldap_get_dnW"); } pCACurrent->m_bstrDN = CertAllocString(wszDN); // ldap_get_dnW rtn value should be freed by calling ldap_memfree ldap_memfree(wszDN); // check success of CertAllocString if(pCACurrent->m_bstrDN == NULL) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "CertAllocString"); } // Add text properties from // DS lookup. for (pwszProp = g_awszCANamedProps; *pwszProp != NULL; pwszProp++) { pProp = new CCAProperty(*pwszProp); if(pProp == NULL) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "new"); } wszLdapVal = ldap_get_values(pld, Entry, *pwszProp); hr = pProp->SetValue(wszLdapVal); _PrintIfError(hr, "SetValue"); if(wszLdapVal) { ldap_value_free(wszLdapVal); } if(hr == S_OK) { hr = CCAProperty::Append(&pCACurrent->m_pProperties, pProp); _PrintIfError(hr, "Append"); } if(hr != S_OK) { CCAProperty::DeleteChain(&pProp); _JumpError(hr, error, "SetValue or Append"); } } pCACurrent->m_dwFlags = dwCAFlags; // Append the security descriptor... apSD = ldap_get_values_len(pld, Entry, LDAP_SECURITY_DESCRIPTOR_NAME); if(apSD != NULL) { pCACurrent->m_pSD = LocalAlloc(LMEM_FIXED, (*apSD)->bv_len); if(pCACurrent->m_pSD == NULL) { hr = E_OUTOFMEMORY; ldap_value_free_len(apSD); _JumpError(hr, error, "LocalAlloc"); } CopyMemory(pCACurrent->m_pSD, (*apSD)->bv_val, (*apSD)->bv_len); ldap_value_free_len(apSD); } pCACurrent->m_fNew = FALSE; } // May be null if none found. *ppCAInfo = pCAFirst; pCAFirst = NULL; error: if(SearchResult) { ldap_msgfree(SearchResult); } if (NULL != pCAFirst) { delete pCAFirst; } if(pCert) { CertFreeCertificateContext(pCert); } if(pChainContext) { CertFreeCertificateChain(pChainContext); } return(hr); } //+-------------------------------------------------------------------------- // CCAInfo::Create -- Create CA Object in the DS // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::Create( LPCWSTR wszName, LPCWSTR wszScope, CCAInfo **ppCAInfo) { HRESULT hr = S_OK; CCAInfo *pCACurrent = NULL; LDAP * pld = NULL; // Initialize LDAP session DWORD cFullLocation; CERTSTR bstrScope = NULL; LPWSTR cnVals[2]; CCAProperty *pProp; if (NULL == ppCAInfo || NULL == wszName) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } // short circuit calls to a nonexistant DS hr = myDoesDSExist(TRUE); _JumpIfError(hr, error, "myDoesDSExist"); __try { // bind to ds hr = myRobustLdapBindEx( 0, // dwFlags1 RLBF_REQUIRE_SECURE_LDAP, // dwFlags2 LDAP_VERSION2, // uVersion NULL, // pwszDomainName &pld, NULL); // ppwszForestDNSName _LeaveIfError(hr, "myRobustLdapBindEx"); if(wszScope) { bstrScope = CertAllocString(wszScope); if(bstrScope == NULL) { hr = E_OUTOFMEMORY; _LeaveError(hr, "CertAllocString"); } } else { // If scope is not specified, set it to the // current domain scope. hr = CAGetAuthoritativeDomainDn(pld, NULL, &bstrScope); if(S_OK != hr) { _LeaveError(hr, "CAGetAuthoritativeDomainDn"); } } pCACurrent = new CCAInfo; if(pCACurrent == NULL) { hr = E_OUTOFMEMORY; _LeaveError(hr, "new"); } cFullLocation = 4 + wcslen(wszName) + wcslen(g_wszEnrollmentServiceLocation) + wcslen(bstrScope); pCACurrent->m_bstrDN = CertAllocStringLen(NULL, cFullLocation); if(pCACurrent->m_bstrDN == NULL) { hr = E_OUTOFMEMORY; _LeaveError(hr, "CertAllocStringLen"); } wcscpy(pCACurrent->m_bstrDN, L"CN="); wcscat(pCACurrent->m_bstrDN, wszName); wcscat(pCACurrent->m_bstrDN, L","); wcscat(pCACurrent->m_bstrDN, g_wszEnrollmentServiceLocation); wcscat(pCACurrent->m_bstrDN, bstrScope); pProp = new CCAProperty(CA_PROP_NAME); if (pProp == NULL) { hr = E_OUTOFMEMORY; _LeaveError(hr, "new"); } cnVals[0] = (LPWSTR)wszName; cnVals[1] = NULL; pProp->SetValue(cnVals); CCAProperty::Append(&pCACurrent->m_pProperties, pProp); pCACurrent->m_fNew = TRUE; *ppCAInfo = pCACurrent; pCACurrent = NULL; } __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER) { } error: if (NULL != bstrScope) { CertFreeString(bstrScope); } if (NULL != pld) { ldap_unbind(pld); } if (NULL != pCACurrent) { delete pCACurrent; } return(hr); } HRESULT CCAInfo::Update(VOID) { HRESULT hr = S_OK; ULONG ldaperr; LDAP *pld = NULL; LDAPMod objectClass, cnmod, displaymod, certmod, certdnmod, machinednsmod, certtypesmod, Flagsmod, sdmod, Descriptionmod; WCHAR *awszNull[1] = { NULL }; DWORD cName; TCHAR *objectClassVals[3], *certdnVals[2]; LDAPMod *mods[13]; struct berval certberval; struct berval sdberval; struct berval *certVals[2], *sdVals[2]; CHAR sdBerValue[] = {0x30, 0x03, 0x02, 0x01, DACL_SECURITY_INFORMATION| OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION}; LDAPControl se_info_control = { LDAP_SERVER_SD_FLAGS_OID_W, { 5, sdBerValue }, TRUE }; LDAPControl permissive_modify_control = { LDAP_SERVER_PERMISSIVE_MODIFY_OID_W, { 0, NULL }, FALSE }; PLDAPControl server_controls[3] = { &se_info_control, &permissive_modify_control, NULL }; // for now, modifies don't try to update owner/group CHAR sdBerValueDaclOnly[] = {0x30, 0x03, 0x02, 0x01, DACL_SECURITY_INFORMATION}; LDAPControl se_info_control_dacl_only = { LDAP_SERVER_SD_FLAGS_OID_W, { 5, sdBerValueDaclOnly }, TRUE }; PLDAPControl server_controls_dacl_only[3] = { &se_info_control_dacl_only, &permissive_modify_control, NULL }; WCHAR wszFlags[cwcDWORDSPRINTF], *awszFlags[2]; DWORD iMod = 0; certdnVals[0] = NULL; // Things we free and must put into known state cnmod.mod_values = NULL; displaymod.mod_values = NULL; machinednsmod.mod_values = NULL; certtypesmod.mod_values = NULL; Descriptionmod.mod_values = NULL; if (NULL == m_pCertificate) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } // short circuit calls to a nonexistant DS hr = myDoesDSExist(TRUE); _JumpIfError(hr, error, "myDoesDSExist"); __try { // bind to ds hr = myRobustLdapBindEx( 0, // dwFlags1 RLBF_REQUIRE_SECURE_LDAP, // dwFlags2 LDAP_VERSION2, // uVersion NULL, // pwszDomainName &pld, NULL); // ppwszForestDNSName _LeaveIfError(hr, "myRobustLdapBindEx"); objectClass.mod_op = LDAP_MOD_REPLACE; objectClass.mod_type = TEXT("objectclass"); objectClass.mod_values = objectClassVals; objectClassVals[0] = wszDSTOPCLASSNAME; objectClassVals[1] = wszDSENROLLMENTSERVICECLASSNAME; objectClassVals[2] = NULL; mods[iMod++] = &objectClass; cnmod.mod_op = LDAP_MOD_REPLACE; cnmod.mod_type = CA_PROP_NAME; hr = GetProperty(CA_PROP_NAME, &cnmod.mod_values); if((hr != S_OK) || (cnmod.mod_values == NULL)) { cnmod.mod_values = awszNull; if(!m_fNew) { mods[iMod++] = &cnmod; } } else { mods[iMod++] = &cnmod; } displaymod.mod_op = LDAP_MOD_REPLACE; displaymod.mod_type = CA_PROP_DISPLAY_NAME; hr = GetProperty(CA_PROP_DISPLAY_NAME, &displaymod.mod_values); if((hr != S_OK) || (displaymod.mod_values == NULL)) { displaymod.mod_values = awszNull; if(!m_fNew) { mods[iMod++] = &displaymod; } } else { mods[iMod++] = &displaymod; } Flagsmod.mod_op = LDAP_MOD_REPLACE; Flagsmod.mod_type = CERTTYPE_PROP_FLAGS; Flagsmod.mod_values = awszFlags; awszFlags[0] = wszFlags; awszFlags[1] = NULL; wsprintf(wszFlags, L"%lu", m_dwFlags); mods[iMod++] = &Flagsmod; certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE; certmod.mod_type = TEXT("cACertificate"); certmod.mod_bvalues = certVals; certVals[0] = &certberval; certVals[1] = NULL; certberval.bv_len = m_pCertificate->cbCertEncoded; certberval.bv_val = (char *)m_pCertificate->pbCertEncoded; mods[iMod++] = &certmod; certdnmod.mod_op = LDAP_MOD_REPLACE; certdnmod.mod_type = TEXT("cACertificateDN"); certdnmod.mod_values = certdnVals; cName = CertNameToStr(X509_ASN_ENCODING, &m_pCertificate->pCertInfo->Subject, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, NULL, 0); if (0 == cName) { hr = myHLastError(); _LeaveError(hr, "CertNameToStr"); } certdnVals[0] = CertAllocStringLen(NULL, cName); if( certdnVals[0] == NULL) { hr = E_OUTOFMEMORY; _LeaveError(hr, "CertAllocStringLen"); } if(0 == CertNameToStr(X509_ASN_ENCODING, &m_pCertificate->pCertInfo->Subject, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, certdnVals[0], cName)) { hr = myHLastError(); _LeaveError(hr, "CertNameToStr"); } certdnVals[1] = NULL; mods[iMod++] = &certdnmod; machinednsmod.mod_op = LDAP_MOD_REPLACE; machinednsmod.mod_type = CA_PROP_DNSNAME; hr = GetProperty(CA_PROP_DNSNAME, &machinednsmod.mod_values); if((hr != S_OK) || (machinednsmod.mod_values == NULL)) { machinednsmod.mod_values = awszNull; if(!m_fNew) { mods[iMod++] = &machinednsmod; } } else { mods[iMod++] = &machinednsmod; } certtypesmod.mod_op = LDAP_MOD_REPLACE; certtypesmod.mod_type = LDAP_CERTIFICATE_TEMPLATES_NAME; hr = GetProperty(CA_PROP_CERT_TYPES, &certtypesmod.mod_values); if((hr != S_OK) || (certtypesmod.mod_values == NULL)) { certtypesmod.mod_values = awszNull; if(!m_fNew) { mods[iMod++] = &certtypesmod; } } else { mods[iMod++] = &certtypesmod; } sdmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE; sdmod.mod_type = LDAP_SECURITY_DESCRIPTOR_NAME; sdmod.mod_bvalues = sdVals; sdVals[0] = &sdberval; sdVals[1] = NULL; if(NULL == m_pSD) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_SECURITY_DESCR); _LeaveError(hr, "SecurityDescriptor"); } if(IsValidSecurityDescriptor(m_pSD)) { sdberval.bv_len = GetSecurityDescriptorLength(m_pSD); sdberval.bv_val = (char *)m_pSD; } else { hr = HRESULT_FROM_WIN32(ERROR_INVALID_SECURITY_DESCR); _LeaveError(hr, "SecurityDescriptor"); } mods[iMod++] = &sdmod; Descriptionmod.mod_op = LDAP_MOD_REPLACE; Descriptionmod.mod_type = CA_PROP_DESCRIPTION; hr = GetProperty(CA_PROP_DESCRIPTION, &Descriptionmod.mod_values); if((hr != S_OK) || (Descriptionmod.mod_values == NULL)) { Descriptionmod.mod_values = awszNull; if(!m_fNew) { mods[iMod++] = &Descriptionmod; } } else { mods[iMod++] = &Descriptionmod; } mods[iMod] = NULL; CSASSERT(ARRAYSIZE(mods) > iMod); hr = S_OK; if(m_fNew) { DBGPRINT((DBG_SS_CERTLIBI, "Creating DS PKI Enrollment object: '%ws'\n", m_bstrDN)); ldaperr = ldap_add_ext_sW(pld, m_bstrDN, mods, server_controls, NULL); } else { // don't attempt to set owner/group for pre-existing objects DBGPRINT((DBG_SS_CERTLIBI, "Updating DS PKI Enrollment object: '%ws'\n", m_bstrDN)); ldaperr = ldap_modify_ext_sW(pld, m_bstrDN, &mods[2], server_controls_dacl_only, NULL); // skip past objectClass and cn if(LDAP_ATTRIBUTE_OR_VALUE_EXISTS == ldaperr) { ldaperr = LDAP_SUCCESS; } } if (LDAP_SUCCESS != ldaperr && LDAP_ALREADY_EXISTS != ldaperr) { hr = myHLdapError(pld, ldaperr, NULL); _LeaveError(ldaperr, m_fNew? "ldap_add_s" : "ldap_modify_sW"); } m_fNew = FALSE; } __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER) { } error: if (NULL != certdnVals[0]) { CertFreeString(certdnVals[0]); } if (NULL != certtypesmod.mod_values && awszNull != certtypesmod.mod_values) { FreeProperty(certtypesmod.mod_values); } if (NULL != machinednsmod.mod_values && awszNull != machinednsmod.mod_values) { FreeProperty(machinednsmod.mod_values); } if (NULL != cnmod.mod_values && awszNull != cnmod.mod_values) { FreeProperty(cnmod.mod_values); } if (NULL != displaymod.mod_values && awszNull != displaymod.mod_values) { FreeProperty(displaymod.mod_values); } if (NULL != Descriptionmod.mod_values && awszNull != Descriptionmod.mod_values) { FreeProperty(Descriptionmod.mod_values); } if (NULL != pld) { ldap_unbind(pld); } return(hr); } HRESULT CCAInfo::Delete(VOID) { LDAP *pld = NULL; HRESULT hr = S_OK; DWORD ldaperr; // short circuit calls to a nonexistant DS hr = myDoesDSExist(TRUE); _JumpIfError(hr, error, "myDoesDSExist"); __try { // bind to ds hr = myRobustLdapBindEx( 0, // dwFlags1 RLBF_REQUIRE_SECURE_LDAP, // dwFlags2 LDAP_VERSION2, // uVersion NULL, // pwszDomainName &pld, NULL); // ppwszForestDNSName _LeaveIfError(hr, "myRobustLdapBindEx"); ldaperr = ldap_delete_s(pld, m_bstrDN); hr = myHLdapError(pld, ldaperr, NULL); } __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER) { } error: if (NULL != pld) { ldap_unbind(pld); } return (hr); } //+-------------------------------------------------------------------------- // CCAInfo::FindDnsDomain -- Find CA Objects in the DS, given a scope specified // by a dns domain name. // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::FindDnsDomain( LPCWSTR wszQuery, LPCWSTR wszDnsDomain, DWORD dwFlags, CCAInfo **ppCAInfo) { HRESULT hr = S_OK; DWORD err; WCHAR *wszScope = NULL; DWORD cScope; if (NULL != wszDnsDomain) { cScope = 0; err = DNStoRFC1779Name(NULL, &cScope, wszDnsDomain); if(err != ERROR_INSUFFICIENT_BUFFER) { hr = myHError(err); _JumpError(hr, error, "DNStoRFC1779Name"); } cScope += 1; wszScope = (WCHAR *) LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*cScope); if (NULL == wszScope) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } err = DNStoRFC1779Name(wszScope, &cScope, wszDnsDomain); if (ERROR_SUCCESS != err) { hr = myHError(err); _JumpError(hr, error, "DNStoRFC1779Name"); } } hr = Find(wszQuery, wszScope, dwFlags, ppCAInfo); _JumpIfError4( hr, error, "Find", HRESULT_FROM_WIN32(ERROR_SERVER_DISABLED), HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN), HRESULT_FROM_WIN32(ERROR_WRONG_PASSWORD)); error: if (NULL != wszScope) { LocalFree(wszScope); } return(hr); } //+-------------------------------------------------------------------------- // CCAInfo::CreateDnsDomain -- Find CA Objects in the DS, given a scope specified // by a dns domain name. // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::CreateDnsDomain( LPCWSTR wszName, LPCWSTR wszDnsDomain, CCAInfo **ppCAInfo) { HRESULT hr = S_OK; DWORD err; WCHAR *wszScope = NULL; DWORD cScope; if(wszDnsDomain) { cScope = 0; err = DNStoRFC1779Name(NULL, &cScope, wszDnsDomain); if(err != ERROR_INSUFFICIENT_BUFFER) { hr = myHError(err); _JumpError(hr, error, "DNStoRFC1779Name"); } cScope += 1; wszScope = (WCHAR *) LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*cScope); if (NULL == wszScope) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } err = DNStoRFC1779Name(wszScope, &cScope, wszDnsDomain); if (ERROR_SUCCESS != err) { hr = myHError(err); _JumpError(hr, error, "DNStoRFC1779Name"); } } hr = Create(wszName, wszScope, ppCAInfo); _JumpIfError(hr, error, "Create"); error: if (NULL != wszScope) { LocalFree(wszScope); } return(hr); } //+-------------------------------------------------------------------------- // CCAInfo::Next -- Returns the next object in the chain of CA objects // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::Next(CCAInfo **ppCAInfo) { HRESULT hr; if (NULL == ppCAInfo) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } *ppCAInfo = m_pNext; if (NULL != m_pNext) { m_pNext->AddRef(); } hr = S_OK; error: return(hr); } //+-------------------------------------------------------------------------- // CCAInfo::GetProperty -- Retrieves the values of a property of the CA object // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::GetProperty( LPCWSTR wszPropertyName, LPWSTR **pawszProperties) { HRESULT hr; LPWSTR *awszResult = NULL; LPWSTR pwszName=NULL; CCAProperty *pProp; LPCWSTR wszProp = NULL; if (NULL == wszPropertyName || NULL == pawszProperties) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } if(LSTRCMPIS(wszPropertyName, L"machineDNSName") == 0) { wszProp = CA_PROP_DNSNAME; } else if(LSTRCMPIS(wszPropertyName, L"supportedCertificateTemplates") == 0) { wszProp = CA_PROP_CERT_TYPES; } else if(LSTRCMPIS(wszPropertyName, L"signatureAlgs") == 0) { wszProp = CA_PROP_SIGNATURE_ALGS; } else { wszProp = wszPropertyName; } hr = m_pProperties->Find(wszProp, &pProp); _JumpIfErrorStr(hr, error, "Find", wszProp); if (NULL != pProp) { hr = pProp->GetValue(pawszProperties); _JumpIfError(hr, error, "GetValue"); } else { *pawszProperties = NULL; } if((LSTRCMPIS(wszPropertyName, CA_PROP_DISPLAY_NAME) == 0) && ((*pawszProperties == NULL) || ((*pawszProperties)[0] == NULL))) { // DISPLAY_NAME is empty, so we try to return the display name // of the CA's certificate. if that also failed, just pass back the CN if(*pawszProperties != NULL) { LocalFree(*pawszProperties); *pawszProperties = NULL; } if(m_pCertificate) { DWORD dwChar; dwChar = CertGetNameStringW( m_pCertificate, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0); if (0 != dwChar) { pwszName=(LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (dwChar)); if(NULL==pwszName) { hr=E_OUTOFMEMORY; _JumpIfError(hr, error, "GetPropertyDisplayName"); } dwChar = CertGetNameStringW( m_pCertificate, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, pwszName, dwChar); if (0 != dwChar) { awszResult=(WCHAR **)LocalAlloc(LPTR, (UINT)(sizeof(WCHAR *)*2+(wcslen(pwszName)+1)*sizeof(WCHAR))); if (NULL==awszResult) { hr=E_OUTOFMEMORY; _JumpIfError(hr, error, "GetPropertyDisplayName"); } awszResult[0]=(WCHAR *)(&awszResult[2]); awszResult[1]=NULL; wcscpy(awszResult[0], pwszName); LocalFree(pwszName); *pawszProperties=awszResult; return S_OK; } } } hr = GetProperty(CA_PROP_NAME, pawszProperties); _JumpIfError(hr, error, "GetProperty"); } error: if(pwszName) LocalFree(pwszName); return(hr); } //+-------------------------------------------------------------------------- // CCertTypeInfo::SetProperty -- Sets the value of a property // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::SetProperty( LPCWSTR wszPropertyName, LPWSTR *awszProperties) { HRESULT hr; CCAProperty *pProp; CCAProperty *pNewProp = NULL; if (NULL == wszPropertyName) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } hr = m_pProperties->Find(wszPropertyName, &pProp); if (S_OK != hr) { pNewProp = new CCAProperty(wszPropertyName); if (NULL == pNewProp) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "new"); } hr = pNewProp->SetValue(awszProperties); _JumpIfError(hr, error, "SetValue"); hr = CCAProperty::Append(&m_pProperties, pNewProp); _JumpIfError(hr, error, "Append"); pNewProp = NULL; // remove our reference if we gave it to m_pProperties } else { hr = pProp->SetValue(awszProperties); _JumpIfError(hr, error, "SetValue"); } error: if (NULL != pNewProp) CCAProperty::DeleteChain(&pNewProp); return(hr); } //+-------------------------------------------------------------------------- // CCAInfo::FreeProperty -- Free's a previously returned property array // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::FreeProperty( LPWSTR *pawszProperties) { if (NULL != pawszProperties) { LocalFree(pawszProperties); } return(S_OK); } //+-------------------------------------------------------------------------- // CCAInfo::GetCertificte -- get the CA certificate // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::GetCertificate( PCCERT_CONTEXT *ppCert) { HRESULT hr; if (NULL == ppCert) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } *ppCert = CertDuplicateCertificateContext(m_pCertificate); hr = S_OK; error: return(hr); } //+-------------------------------------------------------------------------- // CCAInfo::SetCertificte -- get the CA certificate // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::SetCertificate( PCCERT_CONTEXT pCert) { if (NULL != m_pCertificate) { CertFreeCertificateContext(m_pCertificate); m_pCertificate = NULL; } if (NULL != pCert) { m_pCertificate = CertDuplicateCertificateContext(pCert); } return S_OK; } //+-------------------------------------------------------------------------- // CCAInfo::EnumCertTypesEx -- Enumerate cert types supported by this CA // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::EnumSupportedCertTypesEx( LPCWSTR wszScope, DWORD dwFlags, CCertTypeInfo **ppCertTypes) { HRESULT hr; LPWSTR * awszCertTypes = NULL; if (NULL == ppCertTypes) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } *ppCertTypes = NULL; hr = GetProperty(CA_PROP_CERT_TYPES, &awszCertTypes); _JumpIfError(hr, error, "GetProperty"); if (NULL != awszCertTypes) { // Build a filter based on all of the global // entries in the cert list. hr = CCertTypeInfo::FindByNames( (LPCWSTR *)awszCertTypes, wszScope, dwFlags, ppCertTypes); _JumpIfError(hr, error, "FindByNames"); } error: if (awszCertTypes) { FreeProperty(awszCertTypes); } return(hr); } //+-------------------------------------------------------------------------- // CCAInfo::EnumCertTypes -- Enumerate cert types supported by this CA // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::EnumSupportedCertTypes( DWORD dwFlags, CCertTypeInfo **ppCertTypes) { return CCAInfo::EnumSupportedCertTypesEx(NULL, dwFlags, ppCertTypes); } //+-------------------------------------------------------------------------- // CCAInfo::AddCertType -- Add a cert type to this CA // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::AddCertType( CCertTypeInfo *pCertType) { HRESULT hr; LPWSTR *awszCertTypes = NULL; LPWSTR *awszCertTypeName = NULL; LPWSTR *awszNewTypes = NULL; LPWSTR wszCertTypeShortName = NULL; DWORD cTypes; if (NULL == pCertType) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } hr = GetProperty(CA_PROP_CERT_TYPES, &awszCertTypes); _JumpIfError(hr, error, "GetProperty"); hr = pCertType->GetProperty(CERTTYPE_PROP_DN, &awszCertTypeName); _JumpIfError(hr, error, "GetProperty"); if (NULL == awszCertTypeName || NULL == awszCertTypeName[0]) { hr = E_POINTER; _JumpError(hr, error, "NULL CertTypeName"); } if((NULL != (wszCertTypeShortName = wcschr(awszCertTypeName[0], L'|'))) || (NULL != (wszCertTypeShortName = wcschr(awszCertTypeName[0], wcRBRACE)))) { wszCertTypeShortName++; } if (NULL == awszCertTypes || NULL == awszCertTypes[0]) { // no templates on the CA, add the new one and exit hr = SetProperty(CA_PROP_CERT_TYPES, awszCertTypeName); _JumpIfError(hr, error, "SetProperty"); } else { // If cert type is already on ca, do nothing for (cTypes = 0; awszCertTypes[cTypes] != NULL; cTypes++) { if (0 == mylstrcmpiL(awszCertTypes[cTypes], awszCertTypeName[0])) { hr = S_OK; goto error; } if(wszCertTypeShortName) { if (0 == mylstrcmpiL(awszCertTypes[cTypes], wszCertTypeShortName)) { hr = S_OK; goto error; } } } awszNewTypes = (WCHAR **) LocalAlloc( LMEM_FIXED, (cTypes + 2) * sizeof(WCHAR *)); if (NULL == awszNewTypes) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } CopyMemory(awszNewTypes, awszCertTypes, cTypes * sizeof(WCHAR *)); awszNewTypes[cTypes] = awszCertTypeName[0]; awszNewTypes[cTypes + 1] = NULL; hr = SetProperty(CA_PROP_CERT_TYPES, awszNewTypes); _JumpIfError(hr, error, "SetProperty"); } error: if (NULL != awszCertTypes) { FreeProperty(awszCertTypes); } if (NULL != awszCertTypeName) { FreeProperty(awszCertTypeName); } if (NULL != awszNewTypes) { LocalFree(awszNewTypes); } return(hr); } //+-------------------------------------------------------------------------- // CCAInfo::RemoveCertType -- Remove a cert type from this CA // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::RemoveCertType( CCertTypeInfo *pCertType) { HRESULT hr; WCHAR **awszCertTypes = NULL; WCHAR **awszCertTypeName = NULL; DWORD cTypes, cTypesNew; LPWSTR wszCertTypeName = NULL; LPWSTR wszCurrentCertTypeName = NULL; if (NULL == pCertType) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } hr = GetProperty(CA_PROP_CERT_TYPES, &awszCertTypes); _JumpIfError(hr, error, "GetProperty"); hr = pCertType->GetProperty(CERTTYPE_PROP_CN, &awszCertTypeName); _JumpIfError(hr, error, "GetProperty"); if (NULL == awszCertTypeName || NULL == awszCertTypeName[0]) { hr = E_POINTER; _JumpError(hr, error, "NULL CertTypeName"); } if (NULL == awszCertTypes || NULL == awszCertTypes[0]) { hr = S_OK; goto error; } wszCertTypeName = wcschr(awszCertTypeName[0], wcRBRACE); if(wszCertTypeName != NULL) { wszCertTypeName++; } else { wszCertTypeName = awszCertTypeName[0]; } cTypesNew = 0; // If cert type is already on ca, do nothing for (cTypes = 0; awszCertTypes[cTypes] != NULL; cTypes++) { if((NULL != (wszCurrentCertTypeName = wcschr(awszCertTypes[cTypes], L'|'))) || (NULL != (wszCurrentCertTypeName = wcschr(awszCertTypes[cTypes], wcRBRACE)))) { wszCurrentCertTypeName++; } else { wszCurrentCertTypeName = awszCertTypes[cTypes]; } if (0 != mylstrcmpiL(wszCurrentCertTypeName, wszCertTypeName)) { awszCertTypes[cTypesNew++] = awszCertTypes[cTypes]; } } awszCertTypes[cTypesNew] = NULL; hr = SetProperty(CA_PROP_CERT_TYPES, awszCertTypes); _JumpIfError(hr, error, "SetProperty"); error: if (NULL != awszCertTypes) { FreeProperty(awszCertTypes); } if (NULL != awszCertTypeName) { FreeProperty(awszCertTypeName); } return(hr); } //+-------------------------------------------------------------------------- // CCAInfo::GetExpiration -- Get the expiration period // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::GetExpiration( DWORD *pdwExpiration, DWORD *pdwUnits) { HRESULT hr; if (NULL == pdwExpiration || NULL == pdwUnits) { hr = E_POINTER; _JumpError(hr, error, "NULL parm"); } *pdwExpiration = m_dwExpiration; *pdwUnits = m_dwExpUnits; hr = S_OK; error: return(hr); } //+-------------------------------------------------------------------------- // CCAInfo::SetExpiration -- Set the expiration period // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::SetExpiration( DWORD dwExpiration, DWORD dwUnits) { m_dwExpiration = dwExpiration; m_dwExpUnits = dwUnits; return(S_OK); } //+-------------------------------------------------------------------------- // CCAInfo::GetSecurity -- // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::GetSecurity(PSECURITY_DESCRIPTOR * ppSD) { PSECURITY_DESCRIPTOR pResult = NULL; DWORD cbSD; if(ppSD == NULL) { return E_POINTER; } if(m_pSD == NULL) { *ppSD = NULL; return S_OK; } if(!IsValidSecurityDescriptor(m_pSD)) { return HRESULT_FROM_WIN32(ERROR_INVALID_SECURITY_DESCR); } cbSD = GetSecurityDescriptorLength(m_pSD); pResult = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, cbSD); if(pResult == NULL) { return E_OUTOFMEMORY; } CopyMemory(pResult, m_pSD, cbSD); *ppSD = pResult; return S_OK; } //+-------------------------------------------------------------------------- // CCAInfo::GetSecurity -- // // //+-------------------------------------------------------------------------- HRESULT CCAInfo::SetSecurity(PSECURITY_DESCRIPTOR pSD) { PSECURITY_DESCRIPTOR pResult = NULL; DWORD cbSD; if(pSD == NULL) { return E_POINTER; } if(!IsValidSecurityDescriptor(pSD)) { return HRESULT_FROM_WIN32(ERROR_INVALID_SECURITY_DESCR); } cbSD = GetSecurityDescriptorLength(pSD); pResult = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, cbSD); if(pResult == NULL) { return E_OUTOFMEMORY; } CopyMemory(pResult, pSD, cbSD); if(m_pSD) { LocalFree(m_pSD); } m_pSD = pResult; return S_OK; } HRESULT CCAInfo::AccessCheck(HANDLE ClientToken, DWORD dwOption) { return CAAccessCheckpEx(ClientToken, m_pSD, dwOption); }