Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3184 lines
77 KiB

  1. //+--------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1996 - 1999
  5. //
  6. // File: ldap.cpp
  7. //
  8. //---------------------------------------------------------------------------
  9. #include <pch.cpp>
  10. #pragma hdrstop
  11. #undef LdapMapErrorToWin32
  12. #include <winldap.h>
  13. #define LdapMapErrorToWin32 Use_myHLdapError_Instead_Of_LdapMapErrorToWin32
  14. #include <ntldap.h>
  15. #include "csldap.h"
  16. #include "certacl.h"
  17. #include "certtype.h"
  18. #include "cainfop.h"
  19. #include "csber.h"
  20. #include "tptrlist.h"
  21. #define __dwFILE__ __dwFILE_CERTLIB_LDAP_CPP__
  22. static CHAR s_sdBerValue[] = {
  23. BER_SEQUENCE,
  24. 3 * sizeof(BYTE), // three byte sequence
  25. BER_INTEGER,
  26. 1 * sizeof(BYTE), // of one-byte integer
  27. DACL_SECURITY_INFORMATION
  28. //OWNER_SECURITY_INFORMATION |
  29. //GROUP_SECURITY_INFORMATION
  30. };
  31. static LDAPControl s_se_info_control =
  32. {
  33. LDAP_SERVER_SD_FLAGS_OID_W,
  34. { ARRAYSIZE(s_sdBerValue), s_sdBerValue },
  35. TRUE
  36. };
  37. LDAPControl *g_rgLdapControls[2] = { &s_se_info_control, NULL };
  38. // Revocation templates
  39. WCHAR const g_wszHTTPRevocationURLTemplate[] = // Fetch CRL via http:
  40. L"http://"
  41. wszFCSAPARM_SERVERDNSNAME
  42. L"/CertEnroll/"
  43. wszFCSAPARM_SANITIZEDCANAME
  44. wszFCSAPARM_CRLFILENAMESUFFIX
  45. wszFCSAPARM_CRLDELTAFILENAMESUFFIX
  46. L".crl";
  47. WCHAR const g_wszFILERevocationURLTemplate[] = // Fetch CRL via file:
  48. L"file://\\\\"
  49. wszFCSAPARM_SERVERDNSNAME
  50. L"\\CertEnroll\\"
  51. wszFCSAPARM_SANITIZEDCANAME
  52. wszFCSAPARM_CRLFILENAMESUFFIX
  53. wszFCSAPARM_CRLDELTAFILENAMESUFFIX
  54. L".crl";
  55. WCHAR const g_wszASPRevocationURLTemplate[] = // ASP revocation check via https:
  56. L"https://"
  57. wszFCSAPARM_SERVERDNSNAME
  58. L"/CertEnroll/nsrev_"
  59. wszFCSAPARM_SANITIZEDCANAME
  60. L".asp";
  61. #define wszCDPDNTEMPLATE \
  62. L"CN=" \
  63. wszFCSAPARM_SANITIZEDCANAMEHASH \
  64. wszFCSAPARM_CRLFILENAMESUFFIX \
  65. L"," \
  66. L"CN=" \
  67. wszFCSAPARM_SERVERSHORTNAME \
  68. L"," \
  69. L"CN=CDP," \
  70. L"CN=Public Key Services," \
  71. L"CN=Services," \
  72. wszFCSAPARM_CONFIGDN
  73. WCHAR const g_wszzLDAPRevocationURLTemplate[] = // Fetch CRL via ldap:
  74. L"ldap:///"
  75. wszCDPDNTEMPLATE
  76. wszFCSAPARM_DSCRLATTRIBUTE
  77. L"\0";
  78. // Publish CRL via ldap:
  79. WCHAR const g_wszCDPDNTemplate[] = wszCDPDNTEMPLATE;
  80. // AIA templates
  81. WCHAR const g_wszHTTPIssuerCertURLTemplate[] = // Fetch CA Cert via http:
  82. L"http://"
  83. wszFCSAPARM_SERVERDNSNAME
  84. L"/CertEnroll/"
  85. wszFCSAPARM_SERVERDNSNAME
  86. L"_"
  87. wszFCSAPARM_SANITIZEDCANAME
  88. wszFCSAPARM_CERTFILENAMESUFFIX
  89. L".crt"
  90. L"\0";
  91. WCHAR const g_wszFILEIssuerCertURLTemplate[] = // Fetch CA Cert via http:
  92. L"file://\\\\"
  93. wszFCSAPARM_SERVERDNSNAME
  94. L"\\CertEnroll\\"
  95. wszFCSAPARM_SERVERDNSNAME
  96. L"_"
  97. wszFCSAPARM_SANITIZEDCANAME
  98. wszFCSAPARM_CERTFILENAMESUFFIX
  99. L".crt"
  100. L"\0";
  101. #define wszAIADNTEMPLATE \
  102. L"CN=" \
  103. wszFCSAPARM_SANITIZEDCANAMEHASH \
  104. L"," \
  105. L"CN=AIA," \
  106. L"CN=Public Key Services," \
  107. L"CN=Services," \
  108. wszFCSAPARM_CONFIGDN
  109. WCHAR const g_wszzLDAPIssuerCertURLTemplate[] = // Fetch CA Cert via ldap:
  110. L"ldap:///"
  111. wszAIADNTEMPLATE
  112. wszFCSAPARM_DSCACERTATTRIBUTE
  113. L"\0";
  114. // Publish CA Cert via ldap:
  115. WCHAR const g_wszAIADNTemplate[] = wszAIADNTEMPLATE;
  116. #define wszNTAUTHDNTEMPLATE \
  117. L"CN=NTAuthCertificates," \
  118. L"CN=Public Key Services," \
  119. L"CN=Services," \
  120. wszFCSAPARM_CONFIGDN
  121. WCHAR const g_wszLDAPNTAuthURLTemplate[] = // Fetch NTAuth Certs via ldap:
  122. L"ldap:///"
  123. wszNTAUTHDNTEMPLATE
  124. wszFCSAPARM_DSCACERTATTRIBUTE;
  125. #define wszROOTTRUSTDNTEMPLATE \
  126. L"CN=" \
  127. wszFCSAPARM_SANITIZEDCANAMEHASH \
  128. L"," \
  129. L"CN=Certification Authorities," \
  130. L"CN=Public Key Services," \
  131. L"CN=Services," \
  132. wszFCSAPARM_CONFIGDN
  133. WCHAR const g_wszLDAPRootTrustURLTemplate[] = // Fetch Root Certs via ldap:
  134. L"ldap:///"
  135. wszROOTTRUSTDNTEMPLATE
  136. wszFCSAPARM_DSCACERTATTRIBUTE;
  137. #define wszKRADNTEMPLATE \
  138. L"CN=" \
  139. wszFCSAPARM_SANITIZEDCANAMEHASH \
  140. L"," \
  141. L"CN=KRA," \
  142. L"CN=Public Key Services," \
  143. L"CN=Services," \
  144. wszFCSAPARM_CONFIGDN
  145. WCHAR const g_wszzLDAPKRACertURLTemplate[] = // Fetch KRA Cert via ldap:
  146. L"ldap:///"
  147. wszKRADNTEMPLATE
  148. wszFCSAPARM_DSKRACERTATTRIBUTE
  149. L"\0";
  150. // Publish KRA Certs via ldap:
  151. WCHAR const g_wszKRADNTemplate[] = wszKRADNTEMPLATE;
  152. DWORD
  153. myGetLDAPFlags()
  154. {
  155. HRESULT hr;
  156. DWORD LDAPFlags;
  157. hr = myGetCertRegDWValue(NULL, NULL, NULL, wszREGLDAPFLAGS, &LDAPFlags);
  158. _PrintIfErrorStr2(
  159. hr,
  160. "myGetCertRegDWValue",
  161. wszREGLDAPFLAGS,
  162. HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
  163. if (S_OK != hr)
  164. {
  165. LDAPFlags = 0;
  166. }
  167. return(LDAPFlags);
  168. }
  169. //+--------------------------------------------------------------------------
  170. //
  171. // Routine Description:
  172. // This routine simply queries the operational attributes for the
  173. // domaindn and configdn.
  174. //
  175. // The strings returned by this routine must be freed by the caller
  176. // using SysFreeString
  177. //
  178. // Parameters:
  179. // pld -- a valid handle to an ldap session
  180. // pstrDomainDN -- a pointer to a string to be allocated in this routine
  181. // pstrConfigDN -- a pointer to a string to be allocated in this routine
  182. //
  183. // Return Values:
  184. // HRESULT for operation error.
  185. //
  186. //---------------------------------------------------------------------------
  187. HRESULT
  188. myGetAuthoritativeDomainDn(
  189. IN LDAP *pld,
  190. OPTIONAL OUT BSTR *pstrDomainDN,
  191. OPTIONAL OUT BSTR *pstrConfigDN)
  192. {
  193. HRESULT hr;
  194. LDAPMessage *pSearchResult = NULL;
  195. LDAPMessage *pEntry;
  196. LDAP_TIMEVAL timeval;
  197. WCHAR *pwszAttrName;
  198. BerElement *pber;
  199. WCHAR **rgpwszValues;
  200. WCHAR *apwszAttrArray[3];
  201. WCHAR *pwszDefaultNamingContext = LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W;
  202. WCHAR *pwszConfigurationNamingContext = LDAP_OPATT_CONFIG_NAMING_CONTEXT_W;
  203. BSTR strDomainDN = NULL;
  204. BSTR strConfigDN = NULL;
  205. // Set the OUT parameters to NULL
  206. if (NULL != pstrConfigDN)
  207. {
  208. *pstrConfigDN = NULL;
  209. }
  210. if (NULL != pstrDomainDN)
  211. {
  212. *pstrDomainDN = NULL;
  213. }
  214. // Query for the ldap server oerational attributes to obtain the default
  215. // naming context.
  216. apwszAttrArray[0] = pwszDefaultNamingContext;
  217. apwszAttrArray[1] = pwszConfigurationNamingContext;
  218. apwszAttrArray[2] = NULL; // this is the sentinel
  219. timeval.tv_sec = csecLDAPTIMEOUT;
  220. timeval.tv_usec = 0;
  221. hr = ldap_search_st(
  222. pld,
  223. NULL, // base
  224. LDAP_SCOPE_BASE,
  225. L"objectClass=*",
  226. apwszAttrArray,
  227. FALSE, // attrsonly
  228. &timeval,
  229. &pSearchResult);
  230. hr = myHLdapError(pld, hr, NULL);
  231. _JumpIfError(hr, error, "ldap_search_st");
  232. pEntry = ldap_first_entry(pld, pSearchResult);
  233. if (NULL == pEntry)
  234. {
  235. hr = myHLdapLastError(pld, NULL);
  236. _JumpError(hr, error, "ldap_first_entry");
  237. }
  238. pwszAttrName = ldap_first_attribute(pld, pEntry, &pber);
  239. while (NULL != pwszAttrName)
  240. {
  241. BSTR *pstr = NULL;
  242. if (NULL != pstrDomainDN &&
  243. 0 == mylstrcmpiS(pwszAttrName, pwszDefaultNamingContext))
  244. {
  245. pstr = &strDomainDN;
  246. }
  247. else
  248. if (NULL != pstrConfigDN &&
  249. 0 == mylstrcmpiS(pwszAttrName, pwszConfigurationNamingContext))
  250. {
  251. pstr = &strConfigDN;
  252. }
  253. if (NULL != pstr && NULL == *pstr)
  254. {
  255. rgpwszValues = ldap_get_values(pld, pEntry, pwszAttrName);
  256. if (NULL != rgpwszValues)
  257. {
  258. if (NULL != rgpwszValues[0])
  259. {
  260. *pstr = SysAllocString(rgpwszValues[0]);
  261. if (NULL == *pstr)
  262. {
  263. hr = E_OUTOFMEMORY;
  264. _JumpError(hr, error, "SysAllocString");
  265. }
  266. }
  267. ldap_value_free(rgpwszValues);
  268. }
  269. }
  270. ldap_memfree(pwszAttrName);
  271. pwszAttrName = ldap_next_attribute(pld, pEntry, pber);
  272. }
  273. if ((NULL != pstrDomainDN && NULL == strDomainDN) ||
  274. (NULL != pstrConfigDN && NULL == strConfigDN))
  275. {
  276. // We couldn't get default domain info - bail out
  277. hr = HRESULT_FROM_WIN32(ERROR_CANT_ACCESS_DOMAIN_INFO);
  278. _JumpError(hr, error, "missing domain info");
  279. }
  280. if (NULL != pstrDomainDN)
  281. {
  282. *pstrDomainDN = strDomainDN;
  283. strDomainDN = NULL;
  284. }
  285. if (NULL != pstrConfigDN)
  286. {
  287. *pstrConfigDN = strConfigDN;
  288. strConfigDN = NULL;
  289. }
  290. hr = S_OK;
  291. error:
  292. if (NULL != pSearchResult)
  293. {
  294. ldap_msgfree(pSearchResult);
  295. }
  296. myLdapClose(NULL, strDomainDN, strConfigDN);
  297. return(myHError(hr));
  298. }
  299. HRESULT
  300. myDomainFromDn(
  301. IN WCHAR const *pwszDN,
  302. OPTIONAL OUT WCHAR **ppwszDomainDNS)
  303. {
  304. HRESULT hr;
  305. DWORD cwcOut;
  306. WCHAR *pwszOut;
  307. WCHAR **ppwszExplodedDN = NULL;
  308. WCHAR **ppwsz;
  309. WCHAR wszDC[4];
  310. WCHAR *pwsz;
  311. *ppwszDomainDNS = NULL;
  312. ppwszExplodedDN = ldap_explode_dn(const_cast<WCHAR *>(pwszDN), 0);
  313. if (NULL == ppwszExplodedDN)
  314. {
  315. hr = myHLdapLastError(NULL, NULL);
  316. _JumpError(hr, error, "ldap_explode_dn");
  317. }
  318. cwcOut = 0;
  319. for (ppwsz = ppwszExplodedDN; NULL != *ppwsz; ppwsz++)
  320. {
  321. pwsz = *ppwsz;
  322. wcsncpy(wszDC, pwsz, ARRAYSIZE(wszDC) - 1);
  323. wszDC[ARRAYSIZE(wszDC) - 1] = L'\0';
  324. if (0 == LSTRCMPIS(wszDC, L"DC="))
  325. {
  326. pwsz += ARRAYSIZE(wszDC) - 1;
  327. if (0 != cwcOut)
  328. {
  329. cwcOut++;
  330. }
  331. cwcOut += wcslen(pwsz);
  332. }
  333. }
  334. pwszOut = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwcOut + 1) * sizeof(WCHAR));
  335. if (NULL == pwszOut)
  336. {
  337. hr = E_OUTOFMEMORY;
  338. _JumpError(hr, error, "LocalAlloc");
  339. }
  340. *ppwszDomainDNS = pwszOut;
  341. for (ppwsz = ppwszExplodedDN; NULL != *ppwsz; ppwsz++)
  342. {
  343. pwsz = *ppwsz;
  344. wcsncpy(wszDC, pwsz, ARRAYSIZE(wszDC) - 1);
  345. wszDC[ARRAYSIZE(wszDC) - 1] = L'\0';
  346. if (0 == LSTRCMPIS(wszDC, L"DC="))
  347. {
  348. pwsz += ARRAYSIZE(wszDC) - 1;
  349. if (pwszOut != *ppwszDomainDNS)
  350. {
  351. *pwszOut++ = L'.';
  352. }
  353. wcscpy(pwszOut, pwsz);
  354. pwszOut += wcslen(pwsz);
  355. }
  356. }
  357. CSASSERT(wcslen(*ppwszDomainDNS) == cwcOut);
  358. hr = S_OK;
  359. error:
  360. if (NULL != ppwszExplodedDN)
  361. {
  362. ldap_value_free(ppwszExplodedDN);
  363. }
  364. return(hr);
  365. }
  366. HRESULT
  367. myLdapOpen(
  368. OPTIONAL IN WCHAR const *pwszDomainName,
  369. IN DWORD dwFlags, // RLBF_*
  370. OUT LDAP **ppld,
  371. OPTIONAL OUT BSTR *pstrDomainDN,
  372. OPTIONAL OUT BSTR *pstrConfigDN)
  373. {
  374. HRESULT hr;
  375. LDAP *pld = NULL;
  376. *ppld = NULL;
  377. CSASSERT(NULL == pstrConfigDN || NULL == *pstrConfigDN);
  378. CSASSERT(NULL == pstrDomainDN || NULL == *pstrDomainDN);
  379. hr = myRobustLdapBindEx(
  380. (RLBF_REQUIRE_GC & dwFlags)? RLBF_TRUE : 0, // dwFlags1 (was fGC)
  381. ~RLBF_TRUE & dwFlags, // dwFlags2
  382. LDAP_VERSION2,
  383. pwszDomainName,
  384. &pld,
  385. NULL); // ppwszForestDNSName
  386. _JumpIfError(hr, error, "myRobustLdapBindEx");
  387. // domain and config containers (%5, %6)
  388. hr = myGetAuthoritativeDomainDn(pld, pstrDomainDN, pstrConfigDN);
  389. if (S_OK != hr)
  390. {
  391. hr = myHError(hr);
  392. _JumpError(hr, error, "myGetAuthoritativeDomainDn");
  393. }
  394. *ppld = pld;
  395. pld = NULL;
  396. error:
  397. if (NULL != pld)
  398. {
  399. ldap_unbind(pld);
  400. }
  401. return(hr);
  402. }
  403. VOID
  404. myLdapClose(
  405. OPTIONAL IN LDAP *pld,
  406. OPTIONAL IN BSTR strDomainDN,
  407. OPTIONAL IN BSTR strConfigDN)
  408. {
  409. if (NULL != strDomainDN)
  410. {
  411. SysFreeString(strDomainDN);
  412. }
  413. if (NULL != strConfigDN)
  414. {
  415. SysFreeString(strConfigDN);
  416. }
  417. if (NULL != pld)
  418. {
  419. ldap_unbind(pld);
  420. }
  421. }
  422. BOOL
  423. myLdapRebindRequired(
  424. IN ULONG ldaperrParm,
  425. OPTIONAL IN LDAP *pld)
  426. {
  427. BOOL fRebindRequired = FALSE;
  428. if (LDAP_SERVER_DOWN == ldaperrParm ||
  429. LDAP_UNAVAILABLE == ldaperrParm ||
  430. LDAP_TIMEOUT == ldaperrParm ||
  431. NULL == pld)
  432. {
  433. fRebindRequired = TRUE;
  434. }
  435. else
  436. {
  437. ULONG ldaperr;
  438. VOID *pvReachable = NULL; // clear high bits for 64-bit machines
  439. ldaperr = ldap_get_option(pld, LDAP_OPT_HOST_REACHABLE, &pvReachable);
  440. if (LDAP_SUCCESS != ldaperr || LDAP_OPT_ON != pvReachable)
  441. {
  442. fRebindRequired = TRUE;
  443. }
  444. }
  445. return(fRebindRequired);
  446. }
  447. HRESULT
  448. myLdapGetDSHostName(
  449. IN LDAP *pld,
  450. OUT WCHAR **ppwszHostName)
  451. {
  452. HRESULT hr;
  453. ULONG ldaperr;
  454. ldaperr = ldap_get_option(pld, LDAP_OPT_HOST_NAME, ppwszHostName);
  455. if (LDAP_SUCCESS != ldaperr)
  456. {
  457. *ppwszHostName = NULL;
  458. }
  459. hr = myHLdapError(pld, ldaperr, NULL);
  460. return(hr);
  461. }
  462. HRESULT
  463. myLdapCreateContainer(
  464. IN LDAP *pld,
  465. IN WCHAR const *pwszDN,
  466. IN BOOL fSkipObject, // Does the DN contain a leaf object name
  467. IN DWORD cMaxLevel, // create this many nested containers as needed
  468. IN PSECURITY_DESCRIPTOR pContainerSD,
  469. OPTIONAL OUT WCHAR **ppwszError)
  470. {
  471. HRESULT hr;
  472. WCHAR const *pwsz = pwszDN;
  473. LDAPMod objectClass;
  474. LDAPMod advancedView;
  475. LDAPMod securityDescriptor;
  476. WCHAR *papwszshowInAdvancedViewOnly[2] = { L"TRUE", NULL };
  477. WCHAR *objectClassVals[3];
  478. LDAPMod *mods[4];
  479. struct berval *sdVals[2];
  480. struct berval sdberval;
  481. if (NULL != ppwszError)
  482. {
  483. *ppwszError = NULL;
  484. }
  485. mods[0] = &objectClass;
  486. mods[1] = &advancedView;
  487. mods[2] = &securityDescriptor;
  488. mods[3] = NULL;
  489. objectClass.mod_op = LDAP_MOD_ADD;
  490. objectClass.mod_type = TEXT("objectclass");
  491. objectClass.mod_values = objectClassVals;
  492. advancedView.mod_op = LDAP_MOD_ADD;
  493. advancedView.mod_type = TEXT("showInAdvancedViewOnly");
  494. advancedView.mod_values = papwszshowInAdvancedViewOnly;
  495. objectClassVals[0] = TEXT("top");
  496. objectClassVals[1] = TEXT("container");
  497. objectClassVals[2] = NULL;
  498. securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  499. securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
  500. securityDescriptor.mod_bvalues = sdVals;
  501. sdVals[0] = &sdberval;
  502. sdVals[1] = NULL;
  503. if (IsValidSecurityDescriptor(pContainerSD))
  504. {
  505. sdberval.bv_len = GetSecurityDescriptorLength(pContainerSD);
  506. sdberval.bv_val = (char *)pContainerSD;
  507. }
  508. else
  509. {
  510. sdberval.bv_len = 0;
  511. sdberval.bv_val = NULL;
  512. }
  513. // If the DN passed in was for the full object that goes in the container
  514. // (and not the container itself), skip past the CN for the leaf object.
  515. if (fSkipObject)
  516. {
  517. // Look for the CN of the container for this object.
  518. pwsz = wcsstr(&pwsz[3], L"CN=");
  519. if (NULL == pwsz)
  520. {
  521. // If there was no CN, then we are contained in an OU or DC,
  522. // and we don't need to do the create.
  523. hr = S_OK;
  524. goto error;
  525. }
  526. }
  527. if (0 != wcsncmp(pwsz, L"CN=", 3))
  528. {
  529. // We're not pointing to a simple container, so don't create this DN.
  530. hr = S_OK;
  531. goto error;
  532. }
  533. pwszDN = pwsz;
  534. if (0 != cMaxLevel)
  535. {
  536. pwsz = wcsstr(&pwsz[3], L"CN=");
  537. if (NULL != pwsz)
  538. {
  539. // The remaining DN is a container, so try to create it.
  540. hr = myLdapCreateContainer(
  541. pld,
  542. pwsz,
  543. FALSE,
  544. cMaxLevel - 1,
  545. pContainerSD,
  546. ppwszError);
  547. // ignore access denied errors to allow delegation
  548. if (E_ACCESSDENIED != hr &&
  549. HRESULT_FROM_WIN32(ERROR_DS_INSUFF_ACCESS_RIGHTS) != hr)
  550. {
  551. _JumpIfErrorStr(hr, error, "myLdapCreateContainer", pwsz);
  552. }
  553. if (NULL != ppwszError && NULL != *ppwszError)
  554. {
  555. LocalFree(ppwszError);
  556. *ppwszError = NULL;
  557. }
  558. hr = S_OK;
  559. }
  560. }
  561. DBGPRINT((DBG_SS_CERTLIBI, "Creating DS Container: '%ws'\n", pwszDN));
  562. // Create the container
  563. hr = ldap_add_ext_s(
  564. pld,
  565. const_cast<WCHAR *>(pwszDN),
  566. mods,
  567. g_rgLdapControls,
  568. NULL);
  569. _PrintIfErrorStr2(
  570. hr,
  571. "ldap_add_ext_s(container)",
  572. pwszDN,
  573. LDAP_ALREADY_EXISTS);
  574. if ((HRESULT) LDAP_SUCCESS != hr && (HRESULT) LDAP_ALREADY_EXISTS != hr)
  575. {
  576. hr = myHLdapError(pld, hr, ppwszError);
  577. _JumpIfErrorStr(hr, error, "ldap_add_ext_s(container)", pwszDN);
  578. }
  579. hr = S_OK;
  580. error:
  581. if(S_OK==hr && ppwszError && *ppwszError)
  582. {
  583. LocalFree(ppwszError);
  584. *ppwszError = NULL;
  585. }
  586. return(hr);
  587. }
  588. HRESULT
  589. TrimURLDN(
  590. IN WCHAR const *pwszIn,
  591. OPTIONAL OUT WCHAR **ppwszSuffix,
  592. OUT WCHAR **ppwszDN)
  593. {
  594. HRESULT hr;
  595. DWORD cSlash;
  596. WCHAR *pwsz;
  597. if (NULL != ppwszSuffix)
  598. {
  599. *ppwszSuffix = NULL;
  600. }
  601. *ppwszDN = NULL;
  602. pwsz = wcschr(pwszIn, L':');
  603. if (NULL != pwsz)
  604. {
  605. pwszIn = &pwsz[1];
  606. }
  607. cSlash = 0;
  608. while (L'/' == *pwszIn)
  609. {
  610. pwszIn++;
  611. cSlash++;
  612. }
  613. if (2 == cSlash)
  614. {
  615. while (L'\0' != *pwszIn && L'/' != *pwszIn)
  616. {
  617. pwszIn++;
  618. }
  619. if (L'\0' != *pwszIn)
  620. {
  621. pwszIn++;
  622. }
  623. }
  624. hr = myDupString(pwszIn, ppwszDN);
  625. _JumpIfError(hr, error, "myDupString");
  626. pwsz = wcschr(*ppwszDN, L'?');
  627. if (NULL != pwsz)
  628. {
  629. *pwsz++ = L'\0';
  630. if (NULL != ppwszSuffix)
  631. {
  632. *ppwszSuffix = pwsz;
  633. }
  634. }
  635. CSASSERT(S_OK == hr);
  636. error:
  637. if (S_OK != hr && NULL != *ppwszDN)
  638. {
  639. LocalFree(*ppwszDN);
  640. *ppwszDN = NULL;
  641. }
  642. return(hr);
  643. }
  644. HRESULT
  645. CreateCertObject(
  646. IN LDAP *pld,
  647. IN WCHAR const *pwszDN,
  648. IN DWORD dwObjectType, // LPC_*
  649. OUT DWORD *pdwDisposition,
  650. OPTIONAL OUT WCHAR **ppwszError)
  651. {
  652. HRESULT hr;
  653. PSECURITY_DESCRIPTOR pSD = NULL;
  654. PSECURITY_DESCRIPTOR pContainerSD = NULL;
  655. *pdwDisposition = LDAP_OTHER;
  656. if (NULL != ppwszError)
  657. {
  658. *ppwszError = NULL;
  659. }
  660. // get default DS CA security descriptor
  661. hr = myGetSDFromTemplate(WSZ_DEFAULT_CA_DS_SECURITY, NULL, &pSD);
  662. _JumpIfError(hr, error, "myGetSDFromTemplate");
  663. // get default DS AIA security descriptor
  664. hr = myGetSDFromTemplate(WSZ_DEFAULT_CA_DS_SECURITY, NULL, &pContainerSD);
  665. _JumpIfError(hr, error, "myGetSDFromTemplate");
  666. if (LPC_CREATECONTAINER & dwObjectType)
  667. {
  668. hr = myLdapCreateContainer(
  669. pld,
  670. pwszDN,
  671. TRUE,
  672. 0,
  673. pContainerSD,
  674. ppwszError);
  675. if (E_ACCESSDENIED != hr &&
  676. HRESULT_FROM_WIN32(ERROR_DS_INSUFF_ACCESS_RIGHTS) != hr)
  677. {
  678. _JumpIfError(hr, error, "myLdapCreateContainer");
  679. }
  680. if (NULL != ppwszError && NULL != *ppwszError)
  681. {
  682. LocalFree(ppwszError);
  683. *ppwszError = NULL;
  684. }
  685. }
  686. if (LPC_CREATEOBJECT & dwObjectType)
  687. {
  688. if (NULL != ppwszError && NULL != *ppwszError)
  689. {
  690. LocalFree(*ppwszError);
  691. *ppwszError = NULL;
  692. }
  693. switch (LPC_OBJECTMASK & dwObjectType)
  694. {
  695. case LPC_CAOBJECT:
  696. hr = myLdapCreateCAObject(
  697. pld,
  698. pwszDN,
  699. NULL,
  700. 0,
  701. pSD,
  702. pdwDisposition,
  703. ppwszError);
  704. _JumpIfErrorStr(hr, error, "myLdapCreateCAObject", pwszDN);
  705. break;
  706. case LPC_KRAOBJECT:
  707. case LPC_USEROBJECT:
  708. case LPC_MACHINEOBJECT:
  709. hr = myLdapCreateUserObject(
  710. pld,
  711. pwszDN,
  712. NULL,
  713. 0,
  714. pSD,
  715. dwObjectType,
  716. pdwDisposition,
  717. ppwszError);
  718. _JumpIfErrorStr(hr, error, "myLdapCreateUserObject", pwszDN);
  719. break;
  720. default:
  721. hr = E_INVALIDARG;
  722. _JumpError(hr, error, "dwObjectType");
  723. }
  724. }
  725. hr = S_OK;
  726. error:
  727. if (NULL != pSD)
  728. {
  729. LocalFree(pSD);
  730. }
  731. if (NULL != pContainerSD)
  732. {
  733. LocalFree(pContainerSD);
  734. }
  735. return(hr);
  736. }
  737. HRESULT
  738. AddCertToAttribute(
  739. IN LDAP *pld,
  740. IN CERT_CONTEXT const *pccPublish,
  741. IN WCHAR const *pwszDN,
  742. IN WCHAR const *pwszAttribute,
  743. IN BOOL fDelete,
  744. OUT DWORD *pdwDisposition,
  745. OPTIONAL OUT WCHAR **ppwszError)
  746. {
  747. HRESULT hr;
  748. DWORD cres;
  749. DWORD cber;
  750. DWORD iber;
  751. DWORD i;
  752. LDAP_TIMEVAL timeval;
  753. LDAPMessage *pmsg = NULL;
  754. LDAPMessage *pres;
  755. WCHAR *apwszAttrs[2];
  756. struct berval **ppberval = NULL;
  757. struct berval **prgpberVals = NULL;
  758. FILETIME ft;
  759. BOOL fDeleteExpiredCert = FALSE;
  760. BOOL fFoundCert = FALSE;
  761. *pdwDisposition = LDAP_OTHER;
  762. if (NULL != ppwszError)
  763. {
  764. *ppwszError = NULL;
  765. }
  766. apwszAttrs[0] = const_cast<WCHAR *>(pwszAttribute);
  767. apwszAttrs[1] = NULL;
  768. timeval.tv_sec = csecLDAPTIMEOUT;
  769. timeval.tv_usec = 0;
  770. hr = ldap_search_st(
  771. pld, // ld
  772. const_cast<WCHAR *>(pwszDN), // base
  773. LDAP_SCOPE_BASE, // scope
  774. NULL, // filter
  775. apwszAttrs, // attrs
  776. FALSE, // attrsonly
  777. &timeval, // timeout
  778. &pmsg); // res
  779. if (S_OK != hr)
  780. {
  781. *pdwDisposition = hr;
  782. hr = myHLdapError(pld, hr, ppwszError);
  783. _JumpErrorStr(hr, error, "ldap_search_st", pwszDN);
  784. }
  785. cres = ldap_count_entries(pld, pmsg);
  786. if (0 == cres)
  787. {
  788. // No entries were found.
  789. hr = NTE_NOT_FOUND;
  790. _JumpError(hr, error, "ldap_count_entries");
  791. }
  792. pres = ldap_first_entry(pld, pmsg);
  793. if (NULL == pres)
  794. {
  795. hr = NTE_NOT_FOUND;
  796. _JumpError(hr, error, "ldap_first_entry");
  797. }
  798. ppberval = ldap_get_values_len(
  799. pld,
  800. pres,
  801. const_cast<WCHAR *>(pwszAttribute));
  802. cber = 0;
  803. if (NULL != ppberval)
  804. {
  805. while (NULL != ppberval[cber])
  806. {
  807. cber++;
  808. }
  809. }
  810. prgpberVals = (struct berval **) LocalAlloc(
  811. LMEM_FIXED,
  812. (cber + 2) * sizeof(prgpberVals[0]));
  813. if (NULL == prgpberVals)
  814. {
  815. hr = E_OUTOFMEMORY;
  816. _JumpError(hr, error, "LocalAlloc");
  817. }
  818. // Delete any certs that are at least one day old
  819. GetSystemTimeAsFileTime(&ft);
  820. myMakeExprDateTime(&ft, -1, ENUM_PERIOD_DAYS);
  821. iber = 0;
  822. if (NULL != ppberval)
  823. {
  824. for (i = 0; NULL != ppberval[i]; i++)
  825. {
  826. BOOL fCopyBER = TRUE;
  827. struct berval *pberval = ppberval[i];
  828. if (pberval->bv_len == 1 && pberval->bv_val[0] == 0)
  829. {
  830. fCopyBER = FALSE; // remove zero byte placeholder value
  831. }
  832. else
  833. if (pccPublish->cbCertEncoded == pberval->bv_len &&
  834. 0 == memcmp(
  835. pberval->bv_val,
  836. pccPublish->pbCertEncoded,
  837. pccPublish->cbCertEncoded))
  838. {
  839. fCopyBER = FALSE; // remove duplicates to avoid ldap error
  840. fFoundCert = TRUE;
  841. }
  842. else
  843. {
  844. CERT_CONTEXT const *pcc;
  845. pcc = CertCreateCertificateContext(
  846. X509_ASN_ENCODING,
  847. (BYTE *) pberval->bv_val,
  848. pberval->bv_len);
  849. if (NULL == pcc)
  850. {
  851. hr = myHLastError();
  852. _PrintError(hr, "CertCreateCertificateContext");
  853. }
  854. else
  855. {
  856. if (0 > CompareFileTime(&pcc->pCertInfo->NotAfter, &ft))
  857. {
  858. fCopyBER = FALSE;
  859. fDeleteExpiredCert = TRUE;
  860. DBGPRINT((DBG_SS_CERTLIB, "Deleting expired cert %u\n", i));
  861. }
  862. CertFreeCertificateContext(pcc);
  863. }
  864. }
  865. if (fCopyBER)
  866. {
  867. prgpberVals[iber++] = pberval;
  868. }
  869. }
  870. }
  871. // set disposition assuming there's nothing to do:
  872. *pdwDisposition = LDAP_ATTRIBUTE_OR_VALUE_EXISTS;
  873. if ((!fFoundCert ^ fDelete) || fDeleteExpiredCert)
  874. {
  875. struct berval certberval;
  876. LDAPMod *mods[2];
  877. LDAPMod certmod;
  878. BYTE bZero = 0;
  879. mods[0] = &certmod;
  880. mods[1] = NULL;
  881. certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  882. certmod.mod_type = const_cast<WCHAR *>(pwszAttribute);
  883. certmod.mod_bvalues = prgpberVals;
  884. if (fDelete)
  885. {
  886. if (0 == iber)
  887. {
  888. certberval.bv_val = (char *) &bZero;
  889. certberval.bv_len = sizeof(bZero);
  890. prgpberVals[iber++] = &certberval;
  891. }
  892. }
  893. else
  894. {
  895. certberval.bv_val = (char *) pccPublish->pbCertEncoded;
  896. certberval.bv_len = pccPublish->cbCertEncoded;
  897. prgpberVals[iber++] = &certberval;
  898. }
  899. prgpberVals[iber] = NULL;
  900. hr = ldap_modify_ext_s(
  901. pld,
  902. const_cast<WCHAR *>(pwszDN),
  903. mods,
  904. NULL,
  905. NULL);
  906. *pdwDisposition = hr;
  907. if (hr != S_OK)
  908. {
  909. hr = myHLdapError(pld, hr, ppwszError);
  910. _JumpError(hr, error, "ldap_modify_ext_s");
  911. }
  912. }
  913. hr = S_OK;
  914. error:
  915. if (NULL != prgpberVals)
  916. {
  917. LocalFree(prgpberVals);
  918. }
  919. if (NULL != ppberval)
  920. {
  921. ldap_value_free_len(ppberval);
  922. }
  923. if (NULL != pmsg)
  924. {
  925. ldap_msgfree(pmsg);
  926. }
  927. return(hr);
  928. }
  929. HRESULT
  930. AddCRLToAttribute(
  931. IN LDAP *pld,
  932. IN CRL_CONTEXT const *pCRLPublish,
  933. IN WCHAR const *pwszDN,
  934. IN WCHAR const *pwszAttribute,
  935. OUT DWORD *pdwDisposition,
  936. OPTIONAL OUT WCHAR **ppwszError)
  937. {
  938. HRESULT hr;
  939. DWORD cres;
  940. LDAP_TIMEVAL timeval;
  941. LDAPMessage *pmsg = NULL;
  942. LDAPMessage *pres;
  943. WCHAR *apwszAttrs[2];
  944. struct berval **ppberval = NULL;
  945. LDAPMod crlmod;
  946. LDAPMod *mods[2];
  947. struct berval *crlberVals[2];
  948. struct berval crlberval;
  949. *pdwDisposition = LDAP_OTHER;
  950. if (NULL != ppwszError)
  951. {
  952. *ppwszError = NULL;
  953. }
  954. apwszAttrs[0] = const_cast<WCHAR *>(pwszAttribute);
  955. apwszAttrs[1] = NULL;
  956. timeval.tv_sec = csecLDAPTIMEOUT;
  957. timeval.tv_usec = 0;
  958. hr = ldap_search_st(
  959. pld, // ld
  960. const_cast<WCHAR *>(pwszDN), // base
  961. LDAP_SCOPE_BASE, // scope
  962. NULL, // filter
  963. apwszAttrs, // attrs
  964. FALSE, // attrsonly
  965. &timeval, // timeout
  966. &pmsg); // res
  967. if (S_OK != hr)
  968. {
  969. *pdwDisposition = hr;
  970. hr = myHLdapError(pld, hr, ppwszError);
  971. _JumpErrorStr(hr, error, "ldap_search_st", pwszDN);
  972. }
  973. cres = ldap_count_entries(pld, pmsg);
  974. if (0 == cres)
  975. {
  976. // No entries were found.
  977. hr = NTE_NOT_FOUND;
  978. _JumpError(hr, error, "ldap_count_entries");
  979. }
  980. pres = ldap_first_entry(pld, pmsg);
  981. if (NULL == pres)
  982. {
  983. hr = NTE_NOT_FOUND;
  984. _JumpError(hr, error, "ldap_first_entry");
  985. }
  986. ppberval = ldap_get_values_len(
  987. pld,
  988. pres,
  989. const_cast<WCHAR *>(pwszAttribute));
  990. if (NULL != ppberval &&
  991. NULL != ppberval[0] &&
  992. pCRLPublish->cbCrlEncoded == ppberval[0]->bv_len &&
  993. 0 == memcmp(
  994. ppberval[0]->bv_val,
  995. pCRLPublish->pbCrlEncoded,
  996. pCRLPublish->cbCrlEncoded))
  997. {
  998. // set disposition assuming there's nothing to do:
  999. *pdwDisposition = LDAP_ATTRIBUTE_OR_VALUE_EXISTS;
  1000. }
  1001. else
  1002. {
  1003. mods[0] = &crlmod;
  1004. mods[1] = NULL;
  1005. crlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  1006. crlmod.mod_type = const_cast<WCHAR *>(pwszAttribute);
  1007. crlmod.mod_bvalues = crlberVals;
  1008. crlberVals[0] = &crlberval;
  1009. crlberVals[1] = NULL;
  1010. crlberval.bv_val = (char *) pCRLPublish->pbCrlEncoded;
  1011. crlberval.bv_len = pCRLPublish->cbCrlEncoded;
  1012. hr = ldap_modify_ext_s(
  1013. pld,
  1014. const_cast<WCHAR *>(pwszDN),
  1015. mods,
  1016. NULL,
  1017. NULL);
  1018. *pdwDisposition = hr;
  1019. if (hr != S_OK)
  1020. {
  1021. hr = myHLdapError(pld, hr, ppwszError);
  1022. _JumpError(hr, error, "ldap_modify_ext_s");
  1023. }
  1024. }
  1025. hr = S_OK;
  1026. error:
  1027. if (NULL != ppberval)
  1028. {
  1029. ldap_value_free_len(ppberval);
  1030. }
  1031. if (NULL != pmsg)
  1032. {
  1033. ldap_msgfree(pmsg);
  1034. }
  1035. return(hr);
  1036. }
  1037. HRESULT
  1038. myLdapPublishCertToDS(
  1039. IN LDAP *pld,
  1040. IN CERT_CONTEXT const *pccPublish,
  1041. IN WCHAR const *pwszURL,
  1042. IN WCHAR const *pwszAttribute,
  1043. IN DWORD dwObjectType, // LPC_*
  1044. IN BOOL fDelete,
  1045. OUT DWORD *pdwDisposition,
  1046. OPTIONAL OUT WCHAR **ppwszError)
  1047. {
  1048. HRESULT hr;
  1049. HRESULT hrCreate = S_OK;
  1050. WCHAR *pwszDN = NULL;
  1051. WCHAR *pwszSuffix;
  1052. WCHAR *pwszCreateError = NULL;
  1053. *pdwDisposition = LDAP_OTHER;
  1054. if (NULL != ppwszError)
  1055. {
  1056. *ppwszError = NULL;
  1057. }
  1058. hr = TrimURLDN(pwszURL, &pwszSuffix, &pwszDN);
  1059. _JumpIfError(hr, error, "TrimURLDN");
  1060. if (0 == LSTRCMPIS(pwszAttribute, wszDSUSERCERTATTRIBUTE) ||
  1061. 0 == LSTRCMPIS(pwszAttribute, wszDSKRACERTATTRIBUTE))
  1062. {
  1063. if (LPC_CAOBJECT == (LPC_OBJECTMASK & dwObjectType))
  1064. {
  1065. hr = E_INVALIDARG;
  1066. }
  1067. }
  1068. else
  1069. if (0 == LSTRCMPIS(pwszAttribute, wszDSCACERTATTRIBUTE) ||
  1070. 0 == LSTRCMPIS(pwszAttribute, wszDSCROSSCERTPAIRATTRIBUTE))
  1071. {
  1072. if (LPC_CAOBJECT != (LPC_OBJECTMASK & dwObjectType))
  1073. {
  1074. hr = E_INVALIDARG;
  1075. }
  1076. }
  1077. else
  1078. {
  1079. hr = E_INVALIDARG;
  1080. }
  1081. _JumpIfErrorStr(hr, error, "Bad Cert Attribute", pwszAttribute);
  1082. *pdwDisposition = LDAP_SUCCESS;
  1083. if ((LPC_CREATECONTAINER | LPC_CREATEOBJECT) & dwObjectType)
  1084. {
  1085. hr = CreateCertObject(
  1086. pld,
  1087. pwszDN,
  1088. dwObjectType,
  1089. pdwDisposition,
  1090. &pwszCreateError);
  1091. hrCreate = hr;
  1092. if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) != hr)
  1093. {
  1094. _JumpIfError(hr, error, "CreateCertObject");
  1095. }
  1096. }
  1097. hr = AddCertToAttribute(
  1098. pld,
  1099. pccPublish,
  1100. pwszDN,
  1101. pwszAttribute,
  1102. fDelete,
  1103. pdwDisposition,
  1104. ppwszError);
  1105. _JumpIfError(hr, error, "AddCertToAttribute");
  1106. CSASSERT(NULL == ppwszError || NULL == *ppwszError);
  1107. error:
  1108. if (HRESULT_FROM_WIN32(ERROR_DS_OBJ_NOT_FOUND) == hr &&
  1109. HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) == hrCreate)
  1110. {
  1111. hr = hrCreate;
  1112. }
  1113. if (NULL != pwszCreateError)
  1114. {
  1115. if (S_OK != hr && NULL != ppwszError)
  1116. {
  1117. if (NULL != *ppwszError)
  1118. {
  1119. myPrependString(pwszCreateError, L"", ppwszError);
  1120. }
  1121. else
  1122. {
  1123. *ppwszError = pwszCreateError;
  1124. pwszCreateError = NULL;
  1125. }
  1126. }
  1127. if (NULL != pwszCreateError)
  1128. {
  1129. LocalFree(pwszCreateError);
  1130. }
  1131. }
  1132. if (NULL != pwszDN)
  1133. {
  1134. LocalFree(pwszDN);
  1135. }
  1136. return(hr);
  1137. }
  1138. HRESULT
  1139. myLdapPublishCRLToDS(
  1140. IN LDAP *pld,
  1141. IN CRL_CONTEXT const *pCRLPublish,
  1142. IN WCHAR const *pwszURL,
  1143. IN WCHAR const *pwszAttribute,
  1144. OUT DWORD *pdwDisposition,
  1145. OPTIONAL OUT WCHAR **ppwszError)
  1146. {
  1147. HRESULT hr;
  1148. WCHAR *pwszDN = NULL;
  1149. WCHAR *pwszSuffix;
  1150. PSECURITY_DESCRIPTOR pSD = NULL;
  1151. PSECURITY_DESCRIPTOR pContainerSD = NULL;
  1152. *pdwDisposition = LDAP_OTHER;
  1153. if (NULL != ppwszError)
  1154. {
  1155. *ppwszError = NULL;
  1156. }
  1157. hr = TrimURLDN(pwszURL, &pwszSuffix, &pwszDN);
  1158. _JumpIfError(hr, error, "TrimURLDN");
  1159. if (0 == LSTRCMPIS(pwszAttribute, wszDSBASECRLATTRIBUTE))
  1160. {
  1161. }
  1162. else if (0 == LSTRCMPIS(pwszAttribute, wszDSDELTACRLATTRIBUTE))
  1163. {
  1164. }
  1165. else
  1166. {
  1167. hr = E_INVALIDARG;
  1168. _JumpErrorStr(hr, error, "Bad CRL Attribute", pwszAttribute);
  1169. }
  1170. // get default DS CDP security descriptor
  1171. hr = myGetSDFromTemplate(WSZ_DEFAULT_CDP_DS_SECURITY, SDDL_CERT_SERV_ADMINISTRATORS, &pSD);
  1172. if (S_OK != hr)
  1173. {
  1174. _PrintError(hr, "myGetSDFromTemplate");
  1175. pSD = NULL;
  1176. }
  1177. // get default DS AIA security descriptor
  1178. hr = myGetSDFromTemplate(WSZ_DEFAULT_CA_DS_SECURITY, NULL, &pContainerSD);
  1179. _JumpIfError(hr, error, "myGetSDFromTemplate");
  1180. hr = myLdapCreateContainer(pld, pwszDN, TRUE, 1, pContainerSD, ppwszError);
  1181. if (E_ACCESSDENIED != hr &&
  1182. HRESULT_FROM_WIN32(ERROR_DS_INSUFF_ACCESS_RIGHTS) != hr)
  1183. {
  1184. _JumpIfErrorStr(hr, error, "myLdapCreateContainer", pwszDN);
  1185. }
  1186. if (NULL != ppwszError && NULL != *ppwszError)
  1187. {
  1188. LocalFree(ppwszError);
  1189. *ppwszError = NULL;
  1190. }
  1191. hr = myLdapCreateCDPObject(
  1192. pld,
  1193. pwszDN,
  1194. NULL != pSD? pSD : pContainerSD,
  1195. pdwDisposition,
  1196. ppwszError);
  1197. _JumpIfErrorStr(hr, error, "myLdapCreateCDPObject", pwszDN);
  1198. hr = AddCRLToAttribute(
  1199. pld,
  1200. pCRLPublish,
  1201. pwszDN,
  1202. pwszAttribute,
  1203. pdwDisposition,
  1204. ppwszError);
  1205. _JumpIfError(hr, error, "AddCRLToAttribute");
  1206. error:
  1207. if (NULL != pSD)
  1208. {
  1209. LocalFree(pSD);
  1210. }
  1211. if (NULL != pContainerSD)
  1212. {
  1213. LocalFree(pContainerSD);
  1214. }
  1215. if (NULL != pwszDN)
  1216. {
  1217. LocalFree(pwszDN);
  1218. }
  1219. return(hr);
  1220. }
  1221. BOOL
  1222. DNExists(
  1223. IN LDAP *pld,
  1224. IN WCHAR const *pwszDN)
  1225. {
  1226. ULONG ldaperr;
  1227. BOOL fExists = FALSE;
  1228. LPWSTR pwszAttrArray[2];
  1229. struct l_timeval timeout;
  1230. LDAPMessage *pResult = NULL;
  1231. pwszAttrArray[0] = L"cn";
  1232. pwszAttrArray[1] = NULL;
  1233. timeout.tv_sec = csecLDAPTIMEOUT;
  1234. timeout.tv_usec = 0;
  1235. ldaperr = ldap_search_ext_s(
  1236. pld,
  1237. const_cast<WCHAR *>(pwszDN),
  1238. LDAP_SCOPE_BASE,
  1239. L"objectClass=*",
  1240. pwszAttrArray,
  1241. 1,
  1242. g_rgLdapControls,
  1243. NULL,
  1244. &timeout,
  1245. 0,
  1246. &pResult);
  1247. if (NULL != pResult)
  1248. {
  1249. fExists = LDAP_SUCCESS == ldaperr &&
  1250. 1 == ldap_count_entries(pld, pResult);
  1251. ldap_msgfree(pResult);
  1252. }
  1253. return(fExists);
  1254. }
  1255. HRESULT
  1256. CreateOrUpdateDSObject(
  1257. IN LDAP *pld,
  1258. IN WCHAR const *pwszDN,
  1259. IN LDAPMod **prgmodsCreate,
  1260. OPTIONAL IN LDAPMod **prgmodsUpdate,
  1261. OUT DWORD *pdwDisposition,
  1262. OPTIONAL OUT WCHAR **ppwszError)
  1263. {
  1264. HRESULT hr;
  1265. ULONG ldaperr;
  1266. WCHAR *pwszError = NULL;
  1267. if (NULL != ppwszError)
  1268. {
  1269. *ppwszError = NULL;
  1270. }
  1271. ldaperr = ldap_add_ext_s(
  1272. pld,
  1273. const_cast<WCHAR *>(pwszDN),
  1274. prgmodsCreate,
  1275. g_rgLdapControls,
  1276. NULL);
  1277. *pdwDisposition = ldaperr;
  1278. _PrintIfErrorStr2(ldaperr, "ldap_add_ext_s", pwszDN, LDAP_ALREADY_EXISTS);
  1279. if (LDAP_ALREADY_EXISTS == ldaperr || LDAP_INSUFFICIENT_RIGHTS == ldaperr)
  1280. {
  1281. if (NULL == prgmodsUpdate)
  1282. {
  1283. if (LDAP_INSUFFICIENT_RIGHTS == ldaperr)
  1284. {
  1285. hr = myHLdapError(pld, ldaperr, &pwszError);
  1286. _PrintErrorStr(hr, "ldap_add_ext_s", pwszError);
  1287. if (!DNExists(pld, pwszDN))
  1288. {
  1289. *ppwszError = pwszError;
  1290. pwszError = NULL;
  1291. _JumpErrorStr(hr, error, "ldap_add_ext_s", *ppwszError);
  1292. }
  1293. }
  1294. ldaperr = LDAP_SUCCESS;
  1295. }
  1296. else
  1297. {
  1298. ldaperr = ldap_modify_ext_s(
  1299. pld,
  1300. const_cast<WCHAR *>(pwszDN),
  1301. prgmodsUpdate,
  1302. NULL,
  1303. NULL);
  1304. *pdwDisposition = ldaperr;
  1305. _PrintIfErrorStr2(
  1306. ldaperr,
  1307. "ldap_modify_ext_s",
  1308. pwszDN,
  1309. LDAP_ATTRIBUTE_OR_VALUE_EXISTS);
  1310. if (LDAP_ATTRIBUTE_OR_VALUE_EXISTS == ldaperr)
  1311. {
  1312. ldaperr = LDAP_SUCCESS;
  1313. }
  1314. }
  1315. }
  1316. if (ldaperr != LDAP_SUCCESS)
  1317. {
  1318. hr = myHLdapError(pld, ldaperr, ppwszError);
  1319. _JumpError(hr, error, "Add/Update DS");
  1320. }
  1321. hr = S_OK;
  1322. error:
  1323. if (NULL != pwszError)
  1324. {
  1325. LocalFree(pwszError);
  1326. }
  1327. return(hr);
  1328. }
  1329. HRESULT
  1330. myLdapCreateCAObject(
  1331. IN LDAP *pld,
  1332. IN WCHAR const *pwszDN,
  1333. OPTIONAL IN BYTE const *pbCert,
  1334. IN DWORD cbCert,
  1335. IN PSECURITY_DESCRIPTOR pSD,
  1336. OUT DWORD *pdwDisposition,
  1337. OPTIONAL OUT WCHAR **ppwszError)
  1338. {
  1339. HRESULT hr;
  1340. BYTE ZeroByte = 0;
  1341. LDAPMod objectClass;
  1342. LDAPMod securityDescriptor;
  1343. LDAPMod crlmod;
  1344. LDAPMod arlmod;
  1345. LDAPMod certmod;
  1346. struct berval sdberval;
  1347. struct berval crlberval;
  1348. struct berval arlberval;
  1349. struct berval certberval;
  1350. WCHAR *objectClassVals[3];
  1351. struct berval *sdVals[2];
  1352. struct berval *crlVals[2];
  1353. struct berval *arlVals[2];
  1354. struct berval *certVals[2];
  1355. LDAPMod *mods[6];
  1356. if (NULL != ppwszError)
  1357. {
  1358. *ppwszError = NULL;
  1359. }
  1360. mods[0] = &objectClass;
  1361. mods[1] = &securityDescriptor;
  1362. mods[2] = &crlmod;
  1363. mods[3] = &arlmod;
  1364. mods[4] = &certmod; // must be last!
  1365. mods[5] = NULL;
  1366. objectClass.mod_op = LDAP_MOD_ADD;
  1367. objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
  1368. objectClass.mod_values = objectClassVals;
  1369. objectClassVals[0] = wszDSTOPCLASSNAME;
  1370. objectClassVals[1] = wszDSCACLASSNAME;
  1371. objectClassVals[2] = NULL;
  1372. securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  1373. securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
  1374. securityDescriptor.mod_bvalues = sdVals;
  1375. sdVals[0] = &sdberval;
  1376. sdVals[1] = NULL;
  1377. sdberval.bv_len = 0;
  1378. sdberval.bv_val = NULL;
  1379. if (IsValidSecurityDescriptor(pSD))
  1380. {
  1381. sdberval.bv_len = GetSecurityDescriptorLength(pSD);
  1382. sdberval.bv_val = (char *) pSD;
  1383. }
  1384. crlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  1385. crlmod.mod_type = wszDSBASECRLATTRIBUTE;
  1386. crlmod.mod_bvalues = crlVals;
  1387. crlVals[0] = &crlberval;
  1388. crlVals[1] = NULL;
  1389. crlberval.bv_len = sizeof(ZeroByte);
  1390. crlberval.bv_val = (char *) &ZeroByte;
  1391. arlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  1392. arlmod.mod_type = wszDSAUTHORITYCRLATTRIBUTE;
  1393. arlmod.mod_bvalues = arlVals;
  1394. arlVals[0] = &arlberval;
  1395. arlVals[1] = NULL;
  1396. arlberval.bv_len = sizeof(ZeroByte);
  1397. arlberval.bv_val = (char *) &ZeroByte;
  1398. certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  1399. certmod.mod_type = wszDSCACERTATTRIBUTE;
  1400. certmod.mod_bvalues = certVals;
  1401. certVals[0] = &certberval;
  1402. certVals[1] = NULL;
  1403. certberval.bv_len = sizeof(ZeroByte);
  1404. certberval.bv_val = (char *) &ZeroByte;
  1405. if (NULL != pbCert)
  1406. {
  1407. certberval.bv_len = cbCert;
  1408. certberval.bv_val = (char *) pbCert;
  1409. }
  1410. DBGPRINT((DBG_SS_CERTLIBI, "Creating DS CA Object: '%ws'\n", pwszDN));
  1411. CSASSERT(&certmod == mods[ARRAYSIZE(mods) - 2]);
  1412. hr = CreateOrUpdateDSObject(
  1413. pld,
  1414. pwszDN,
  1415. mods,
  1416. NULL != pbCert? &mods[ARRAYSIZE(mods) - 2] : NULL,
  1417. pdwDisposition,
  1418. ppwszError);
  1419. _JumpIfError(hr, error, "CreateOrUpdateDSObject(CA object)");
  1420. error:
  1421. return(hr);
  1422. }
  1423. HRESULT
  1424. myLdapCreateUserObject(
  1425. IN LDAP *pld,
  1426. IN WCHAR const *pwszDN,
  1427. OPTIONAL IN BYTE const *pbCert,
  1428. IN DWORD cbCert,
  1429. IN PSECURITY_DESCRIPTOR pSD,
  1430. IN DWORD dwObjectType, // LPC_* (but LPC_CREATE* is ignored)
  1431. OUT DWORD *pdwDisposition,
  1432. OPTIONAL OUT WCHAR **ppwszError)
  1433. {
  1434. HRESULT hr;
  1435. BYTE ZeroByte = 0;
  1436. LDAPMod objectClass;
  1437. LDAPMod securityDescriptor;
  1438. LDAPMod certmod;
  1439. struct berval sdberval;
  1440. struct berval certberval;
  1441. WCHAR *objectClassVals[6];
  1442. struct berval *sdVals[2];
  1443. struct berval *certVals[2];
  1444. LDAPMod *mods[4];
  1445. if (NULL != ppwszError)
  1446. {
  1447. *ppwszError = NULL;
  1448. }
  1449. mods[0] = &objectClass;
  1450. mods[1] = &securityDescriptor;
  1451. mods[2] = &certmod; // must be last!
  1452. mods[3] = NULL;
  1453. securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  1454. securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
  1455. securityDescriptor.mod_bvalues = sdVals;
  1456. sdVals[0] = &sdberval;
  1457. sdVals[1] = NULL;
  1458. sdberval.bv_len = 0;
  1459. sdberval.bv_val = NULL;
  1460. if (IsValidSecurityDescriptor(pSD))
  1461. {
  1462. sdberval.bv_len = GetSecurityDescriptorLength(pSD);
  1463. sdberval.bv_val = (char *) pSD;
  1464. }
  1465. objectClass.mod_op = LDAP_MOD_ADD;
  1466. objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
  1467. objectClass.mod_values = objectClassVals;
  1468. DBGCODE(WCHAR const *pwszObjectType);
  1469. switch (LPC_OBJECTMASK & dwObjectType)
  1470. {
  1471. case LPC_CAOBJECT:
  1472. objectClassVals[0] = wszDSTOPCLASSNAME;
  1473. objectClassVals[1] = wszDSCACLASSNAME;
  1474. objectClassVals[2] = NULL;
  1475. DBGCODE(pwszObjectType = L"CA");
  1476. break;
  1477. case LPC_KRAOBJECT:
  1478. objectClassVals[0] = wszDSTOPCLASSNAME;
  1479. objectClassVals[1] = wszDSKRACLASSNAME;
  1480. objectClassVals[2] = NULL;
  1481. DBGCODE(pwszObjectType = L"KRA");
  1482. break;
  1483. case LPC_USEROBJECT:
  1484. objectClassVals[0] = wszDSTOPCLASSNAME;
  1485. objectClassVals[1] = wszDSPERSONCLASSNAME;
  1486. objectClassVals[2] = wszDSORGPERSONCLASSNAME;
  1487. objectClassVals[3] = wszDSUSERCLASSNAME;
  1488. objectClassVals[4] = NULL;
  1489. DBGCODE(pwszObjectType = L"User");
  1490. break;
  1491. case LPC_MACHINEOBJECT:
  1492. objectClassVals[0] = wszDSTOPCLASSNAME;
  1493. objectClassVals[1] = wszDSPERSONCLASSNAME;
  1494. objectClassVals[2] = wszDSORGPERSONCLASSNAME;
  1495. objectClassVals[3] = wszDSUSERCLASSNAME;
  1496. objectClassVals[4] = wszDSMACHINECLASSNAME;
  1497. objectClassVals[5] = NULL;
  1498. DBGCODE(pwszObjectType = L"Machine");
  1499. break;
  1500. default:
  1501. hr = E_INVALIDARG;
  1502. _JumpError(hr, error, "dwObjectType");
  1503. }
  1504. certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  1505. certmod.mod_type = wszDSUSERCERTATTRIBUTE;
  1506. certmod.mod_bvalues = certVals;
  1507. certVals[0] = &certberval;
  1508. certVals[1] = NULL;
  1509. certberval.bv_len = sizeof(ZeroByte);
  1510. certberval.bv_val = (char *) &ZeroByte;
  1511. if (NULL != pbCert)
  1512. {
  1513. certberval.bv_len = cbCert;
  1514. certberval.bv_val = (char *) pbCert;
  1515. }
  1516. DBGPRINT((
  1517. DBG_SS_CERTLIBI,
  1518. "Creating DS %ws Object: '%ws'\n",
  1519. pwszObjectType,
  1520. pwszDN));
  1521. CSASSERT(&certmod == mods[ARRAYSIZE(mods) - 2]);
  1522. hr = CreateOrUpdateDSObject(
  1523. pld,
  1524. pwszDN,
  1525. mods,
  1526. NULL != pbCert? &mods[ARRAYSIZE(mods) - 2] : NULL,
  1527. pdwDisposition,
  1528. ppwszError);
  1529. _JumpIfError(hr, error, "CreateOrUpdateDSObject(KRA object)");
  1530. error:
  1531. return(hr);
  1532. }
  1533. HRESULT
  1534. myLdapCreateCDPObject(
  1535. IN LDAP *pld,
  1536. IN WCHAR const *pwszDN,
  1537. IN PSECURITY_DESCRIPTOR pSD,
  1538. OUT DWORD *pdwDisposition,
  1539. OPTIONAL OUT WCHAR **ppwszError)
  1540. {
  1541. HRESULT hr;
  1542. BYTE ZeroByte = 0;
  1543. LDAPMod objectClass;
  1544. LDAPMod securityDescriptor;
  1545. LDAPMod crlmod;
  1546. LDAPMod drlmod;
  1547. struct berval sdberval;
  1548. struct berval crlberval;
  1549. struct berval drlberval;
  1550. WCHAR *objectClassVals[3];
  1551. struct berval *sdVals[2];
  1552. struct berval *crlVals[2];
  1553. struct berval *drlVals[2];
  1554. LDAPMod *mods[5];
  1555. if (NULL != ppwszError)
  1556. {
  1557. *ppwszError = NULL;
  1558. }
  1559. mods[0] = &objectClass;
  1560. mods[1] = &securityDescriptor;
  1561. mods[2] = &crlmod;
  1562. mods[3] = &drlmod;
  1563. mods[4] = NULL;
  1564. objectClass.mod_op = LDAP_MOD_ADD;
  1565. objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
  1566. objectClass.mod_values = objectClassVals;
  1567. objectClassVals[0] = wszDSTOPCLASSNAME;
  1568. objectClassVals[1] = wszDSCDPCLASSNAME;
  1569. objectClassVals[2] = NULL;
  1570. securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  1571. securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
  1572. securityDescriptor.mod_bvalues = sdVals;
  1573. sdVals[0] = &sdberval;
  1574. sdVals[1] = NULL;
  1575. sdberval.bv_len = 0;
  1576. sdberval.bv_val = NULL;
  1577. if (IsValidSecurityDescriptor(pSD))
  1578. {
  1579. sdberval.bv_len = GetSecurityDescriptorLength(pSD);
  1580. sdberval.bv_val = (char *) pSD;
  1581. }
  1582. crlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  1583. crlmod.mod_type = wszDSBASECRLATTRIBUTE;
  1584. crlmod.mod_bvalues = crlVals;
  1585. crlVals[0] = &crlberval;
  1586. crlVals[1] = NULL;
  1587. crlberval.bv_val = (char *) &ZeroByte;
  1588. crlberval.bv_len = sizeof(ZeroByte);
  1589. drlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  1590. drlmod.mod_type = wszDSDELTACRLATTRIBUTE;
  1591. drlmod.mod_bvalues = drlVals;
  1592. drlVals[0] = &drlberval;
  1593. drlVals[1] = NULL;
  1594. drlberval.bv_val = (char *) &ZeroByte;
  1595. drlberval.bv_len = sizeof(ZeroByte);
  1596. DBGPRINT((DBG_SS_CERTLIBI, "Creating DS CDP Object: '%ws'\n", pwszDN));
  1597. hr = CreateOrUpdateDSObject(
  1598. pld,
  1599. pwszDN,
  1600. mods,
  1601. NULL,
  1602. pdwDisposition,
  1603. ppwszError);
  1604. _JumpIfError(hr, error, "CreateOrUpdateDSObject(CDP object)");
  1605. error:
  1606. return(hr);
  1607. }
  1608. HRESULT
  1609. myLdapCreateOIDObject(
  1610. IN LDAP *pld,
  1611. IN WCHAR const *pwszDN,
  1612. IN DWORD dwType,
  1613. IN WCHAR const *pwszObjId,
  1614. OUT DWORD *pdwDisposition,
  1615. OPTIONAL OUT WCHAR **ppwszError)
  1616. {
  1617. HRESULT hr;
  1618. WCHAR awcType[22];
  1619. LDAPMod objectClass;
  1620. LDAPMod typemod;
  1621. LDAPMod oidmod;
  1622. WCHAR *objectClassVals[3];
  1623. WCHAR *typeVals[2];
  1624. WCHAR *oidVals[2];
  1625. LDAPMod *mods[4];
  1626. if (NULL != ppwszError)
  1627. {
  1628. *ppwszError = NULL;
  1629. }
  1630. mods[0] = &objectClass;
  1631. mods[1] = &typemod;
  1632. mods[2] = &oidmod;
  1633. mods[3] = NULL;
  1634. CSASSERT(CSExpr(4 == ARRAYSIZE(mods)));
  1635. objectClass.mod_op = LDAP_MOD_ADD;
  1636. objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
  1637. objectClass.mod_values = objectClassVals;
  1638. objectClassVals[0] = wszDSTOPCLASSNAME;
  1639. objectClassVals[1] = wszDSOIDCLASSNAME;
  1640. objectClassVals[2] = NULL;
  1641. CSASSERT(CSExpr(3 == ARRAYSIZE(objectClassVals)));
  1642. typemod.mod_op = LDAP_MOD_ADD;
  1643. typemod.mod_type = OID_PROP_TYPE;
  1644. typemod.mod_values = typeVals;
  1645. wsprintf(awcType, L"%u", dwType);
  1646. typeVals[0] = awcType;
  1647. typeVals[1] = NULL;
  1648. CSASSERT(CSExpr(2 == ARRAYSIZE(typeVals)));
  1649. oidmod.mod_op = LDAP_MOD_ADD;
  1650. oidmod.mod_type = OID_PROP_OID;
  1651. oidmod.mod_values = oidVals;
  1652. oidVals[0] = const_cast<WCHAR *>(pwszObjId);
  1653. oidVals[1] = NULL;
  1654. CSASSERT(CSExpr(2 == ARRAYSIZE(oidVals)));
  1655. DBGPRINT((DBG_SS_CERTLIBI, "Creating DS OID Object: '%ws'\n", pwszDN));
  1656. hr = CreateOrUpdateDSObject(
  1657. pld,
  1658. pwszDN,
  1659. mods,
  1660. NULL,
  1661. pdwDisposition,
  1662. ppwszError);
  1663. _JumpIfError(hr, error, "CreateOrUpdateDSObject(OID object)");
  1664. error:
  1665. return(hr);
  1666. }
  1667. HRESULT
  1668. myLdapOIDIsMatchingLangId(
  1669. IN WCHAR const *pwszDisplayName,
  1670. IN DWORD dwLanguageId,
  1671. OUT BOOL *pfLangIdExists)
  1672. {
  1673. DWORD DisplayLangId = _wtoi(pwszDisplayName);
  1674. *pfLangIdExists = FALSE;
  1675. if (iswdigit(*pwszDisplayName) &&
  1676. NULL != wcschr(pwszDisplayName, L',') &&
  1677. DisplayLangId == dwLanguageId)
  1678. {
  1679. *pfLangIdExists = TRUE;
  1680. }
  1681. return(S_OK);
  1682. }
  1683. HRESULT
  1684. myLdapAddOrDeleteOIDDisplayNameToAttribute(
  1685. IN LDAP *pld,
  1686. OPTIONAL IN WCHAR **ppwszOld,
  1687. IN DWORD dwLanguageId,
  1688. OPTIONAL IN WCHAR const *pwszDisplayName,
  1689. IN WCHAR const *pwszDN,
  1690. IN WCHAR const *pwszAttribute,
  1691. OUT DWORD *pdwDisposition,
  1692. OPTIONAL OUT WCHAR **ppwszError)
  1693. {
  1694. HRESULT hr;
  1695. DWORD cname;
  1696. DWORD iname;
  1697. DWORD i;
  1698. WCHAR **ppwszNew = NULL;
  1699. WCHAR *pwszNew = NULL;
  1700. BOOL fDeleteOldName = FALSE;
  1701. BOOL fNewNameMissing;
  1702. *pdwDisposition = LDAP_OTHER;
  1703. if (NULL != ppwszError)
  1704. {
  1705. *ppwszError = NULL;
  1706. }
  1707. if (NULL != pwszDisplayName)
  1708. {
  1709. pwszNew = (WCHAR *) LocalAlloc(
  1710. LMEM_FIXED,
  1711. (cwcDWORDSPRINTF + 1 + wcslen(pwszDisplayName) + 1) *
  1712. sizeof(WCHAR));
  1713. if (NULL == pwszNew)
  1714. {
  1715. hr = E_OUTOFMEMORY;
  1716. _JumpError(hr, error, "LocalAlloc");
  1717. }
  1718. wsprintf(pwszNew, L"%u,%ws", dwLanguageId, pwszDisplayName);
  1719. }
  1720. cname = 0;
  1721. if (NULL != ppwszOld)
  1722. {
  1723. while (NULL != ppwszOld[cname])
  1724. {
  1725. cname++;
  1726. }
  1727. }
  1728. ppwszNew = (WCHAR **) LocalAlloc(
  1729. LMEM_FIXED,
  1730. (cname + 2) * sizeof(ppwszNew[0]));
  1731. if (NULL == ppwszNew)
  1732. {
  1733. hr = E_OUTOFMEMORY;
  1734. _JumpError(hr, error, "LocalAlloc");
  1735. }
  1736. // Delete any display names with matching dwLanguageId
  1737. iname = 0;
  1738. fNewNameMissing = NULL != pwszNew? TRUE : FALSE;
  1739. if (NULL != ppwszOld)
  1740. {
  1741. for (i = 0; NULL != ppwszOld[i]; i++)
  1742. {
  1743. BOOL fCopy = TRUE;
  1744. WCHAR *pwsz = ppwszOld[i];
  1745. // case-sensitive compare:
  1746. if (NULL != pwszNew && 0 == lstrcmp(pwszNew, ppwszOld[i]))
  1747. {
  1748. fCopy = FALSE; // remove duplicates to avoid ldap error
  1749. fNewNameMissing = FALSE;
  1750. }
  1751. else
  1752. {
  1753. BOOL fLangIdExists;
  1754. hr = myLdapOIDIsMatchingLangId(
  1755. pwsz,
  1756. dwLanguageId,
  1757. &fLangIdExists);
  1758. _PrintIfError(hr, "myLdapOIDIsMatchingLangId");
  1759. if (S_OK != hr || fLangIdExists)
  1760. {
  1761. fCopy = FALSE;
  1762. fDeleteOldName = TRUE;
  1763. DBGPRINT((DBG_SS_CERTLIB, "Deleting %ws\n", pwsz));
  1764. }
  1765. }
  1766. if (fCopy)
  1767. {
  1768. ppwszNew[iname++] = pwsz;
  1769. }
  1770. }
  1771. }
  1772. CSASSERT(iname <= cname);
  1773. // set disposition assuming there's nothing to do:
  1774. *pdwDisposition = LDAP_ATTRIBUTE_OR_VALUE_EXISTS;
  1775. if (fNewNameMissing || fDeleteOldName)
  1776. {
  1777. LDAPMod *mods[2];
  1778. LDAPMod namemod;
  1779. mods[0] = &namemod;
  1780. mods[1] = NULL;
  1781. namemod.mod_op = LDAP_MOD_REPLACE;
  1782. namemod.mod_type = const_cast<WCHAR *>(pwszAttribute);
  1783. namemod.mod_values = ppwszNew;
  1784. ppwszNew[iname++] = pwszNew;
  1785. ppwszNew[iname] = NULL;
  1786. hr = ldap_modify_ext_s(
  1787. pld,
  1788. const_cast<WCHAR *>(pwszDN),
  1789. mods,
  1790. NULL,
  1791. NULL);
  1792. *pdwDisposition = hr;
  1793. if (hr != S_OK)
  1794. {
  1795. hr = myHLdapError(pld, hr, ppwszError);
  1796. _JumpError(hr, error, "ldap_modify_ext_s");
  1797. }
  1798. }
  1799. hr = S_OK;
  1800. error:
  1801. if (NULL != pwszNew)
  1802. {
  1803. LocalFree(pwszNew);
  1804. }
  1805. if (NULL != ppwszNew)
  1806. {
  1807. LocalFree(ppwszNew);
  1808. }
  1809. return(hr);
  1810. }
  1811. HRESULT
  1812. myHLdapError3(
  1813. OPTIONAL IN LDAP *pld,
  1814. IN ULONG ldaperrParm,
  1815. IN ULONG ldaperrParmQuiet,
  1816. IN ULONG ldaperrParmQuiet2,
  1817. OPTIONAL OUT WCHAR **ppwszError)
  1818. {
  1819. HRESULT hr = S_OK;
  1820. if (NULL != ppwszError)
  1821. {
  1822. *ppwszError = NULL;
  1823. }
  1824. if (LDAP_SUCCESS != ldaperrParm)
  1825. {
  1826. BOOL fXlat = TRUE;
  1827. ULONG ldaperr;
  1828. WCHAR *pwszError = NULL;
  1829. if (NULL != pld)
  1830. {
  1831. ldaperr = ldap_get_option(pld, LDAP_OPT_SERVER_ERROR, &pwszError);
  1832. if (LDAP_SUCCESS != ldaperr)
  1833. {
  1834. _PrintError(ldaperr, "ldap_get_option(server error)");
  1835. pwszError = NULL;
  1836. }
  1837. ldaperr = ldap_get_option(pld, LDAP_OPT_SERVER_EXT_ERROR, &hr);
  1838. if (LDAP_SUCCESS != ldaperr)
  1839. {
  1840. _PrintError2(
  1841. ldaperr,
  1842. "ldap_get_option(server extended error)",
  1843. ldaperr);
  1844. }
  1845. else
  1846. {
  1847. fXlat = FALSE;
  1848. }
  1849. }
  1850. if (fXlat)
  1851. {
  1852. #undef LdapMapErrorToWin32
  1853. hr = LdapMapErrorToWin32(ldaperrParm);
  1854. #define LdapMapErrorToWin32 Use_myHLdapError_Instead_Of_LdapMapErrorToWin32
  1855. }
  1856. hr = myHError(hr);
  1857. _PrintErrorStr3(
  1858. ldaperrParm,
  1859. "ldaperr",
  1860. pwszError,
  1861. ldaperrParmQuiet,
  1862. ldaperrParmQuiet2);
  1863. if (NULL != ppwszError && NULL != pwszError)
  1864. {
  1865. WCHAR awc[32];
  1866. DWORD cwc;
  1867. wsprintf(awc, L"ldap: 0x%x: ", ldaperrParm);
  1868. cwc = wcslen(awc) + wcslen(pwszError);
  1869. *ppwszError = (WCHAR *) LocalAlloc(
  1870. LMEM_FIXED,
  1871. (cwc + 1) * sizeof(WCHAR));
  1872. if (NULL == *ppwszError)
  1873. {
  1874. _PrintError(E_OUTOFMEMORY, "LocalAlloc");
  1875. }
  1876. else
  1877. {
  1878. wcscpy(*ppwszError, awc);
  1879. wcscat(*ppwszError, pwszError);
  1880. }
  1881. }
  1882. if (NULL != pwszError)
  1883. {
  1884. ldap_memfree(pwszError);
  1885. }
  1886. }
  1887. return(hr);
  1888. }
  1889. HRESULT
  1890. myHLdapError2(
  1891. OPTIONAL IN LDAP *pld,
  1892. IN ULONG ldaperrParm,
  1893. IN ULONG ldaperrParmQuiet,
  1894. OPTIONAL OUT WCHAR **ppwszError)
  1895. {
  1896. return(myHLdapError3(
  1897. pld,
  1898. ldaperrParm,
  1899. ldaperrParmQuiet,
  1900. LDAP_SUCCESS,
  1901. ppwszError));
  1902. }
  1903. HRESULT
  1904. myHLdapError(
  1905. OPTIONAL IN LDAP *pld,
  1906. IN ULONG ldaperrParm,
  1907. OPTIONAL OUT WCHAR **ppwszError)
  1908. {
  1909. return(myHLdapError3(
  1910. pld,
  1911. ldaperrParm,
  1912. LDAP_SUCCESS,
  1913. LDAP_SUCCESS,
  1914. ppwszError));
  1915. }
  1916. HRESULT
  1917. myHLdapLastError(
  1918. OPTIONAL IN LDAP *pld,
  1919. OPTIONAL OUT WCHAR **ppwszError)
  1920. {
  1921. HRESULT hr;
  1922. hr = myHLdapError3(
  1923. pld,
  1924. LdapGetLastError(),
  1925. LDAP_SUCCESS,
  1926. LDAP_SUCCESS,
  1927. ppwszError);
  1928. // must return error
  1929. if (hr == S_OK)
  1930. return E_UNEXPECTED;
  1931. return hr;
  1932. }
  1933. HRESULT
  1934. myLDAPSetStringAttribute(
  1935. IN LDAP *pld,
  1936. IN WCHAR const *pwszDN,
  1937. IN WCHAR const *pwszAttribute,
  1938. IN WCHAR const *pwszValue,
  1939. OUT DWORD *pdwDisposition,
  1940. OPTIONAL OUT WCHAR **ppwszError)
  1941. {
  1942. HRESULT hr;
  1943. LDAPMod *mods[2];
  1944. LDAPMod certmod;
  1945. const WCHAR *ppwszVals[2];
  1946. CAutoLPWSTR pwszDNOnly;
  1947. WCHAR *pwszSuffix; // no free
  1948. hr = TrimURLDN(pwszDN, &pwszSuffix, &pwszDNOnly);
  1949. _JumpIfErrorStr(hr, error, "TrimURLDN", pwszDN);
  1950. mods[0] = &certmod;
  1951. mods[1] = NULL;
  1952. ppwszVals[0] = pwszValue;
  1953. ppwszVals[1] = NULL;
  1954. certmod.mod_op = LDAP_MOD_REPLACE;
  1955. certmod.mod_type = const_cast<WCHAR *>(pwszAttribute);
  1956. certmod.mod_values = const_cast<PWCHAR *>(ppwszVals);
  1957. hr = ldap_modify_ext_s(
  1958. pld,
  1959. pwszDNOnly,
  1960. mods,
  1961. NULL,
  1962. NULL);
  1963. *pdwDisposition = hr;
  1964. if (hr != S_OK)
  1965. {
  1966. hr = myHLdapError(pld, hr, ppwszError);
  1967. _JumpError(hr, error, "ldap_modify_ext_s");
  1968. }
  1969. hr = S_OK;
  1970. error:
  1971. return hr;
  1972. }
  1973. HRESULT
  1974. CurrentUserCanInstallCA(
  1975. bool& fCanInstall)
  1976. {
  1977. HRESULT hr;
  1978. HANDLE hThread = NULL; // no free
  1979. HANDLE hAccessToken = NULL, hDupToken = NULL;
  1980. LDAP *pld = NULL;
  1981. BSTR bstrConfigDN = NULL;
  1982. LPWSTR pwszPKIContainerFilter =
  1983. L"(&(objectClass=container)(CN=Public Key Services))";
  1984. LPWSTR pwszSDAttr = L"nTSecurityDescriptor";
  1985. LPWSTR pwszAttrArray[3];
  1986. LDAPMessage* pResult = NULL;
  1987. LDAPMessage *pEntry;
  1988. struct berval **bervalSD = NULL;
  1989. PSECURITY_DESCRIPTOR pSD; // no free
  1990. GENERIC_MAPPING mapping;
  1991. PRIVILEGE_SET PrivilegeSet;
  1992. DWORD cPrivilegeSet = sizeof(PrivilegeSet);
  1993. DWORD dwGrantedAccess;
  1994. BOOL fAccess = FALSE;
  1995. struct l_timeval timeout;
  1996. CHAR sdBerValue[] = {0x30, 0x03, 0x02, 0x01,
  1997. DACL_SECURITY_INFORMATION |
  1998. OWNER_SECURITY_INFORMATION |
  1999. GROUP_SECURITY_INFORMATION};
  2000. LDAPControl se_info_control =
  2001. {
  2002. LDAP_SERVER_SD_FLAGS_OID_W,
  2003. {
  2004. 5, sdBerValue
  2005. },
  2006. TRUE
  2007. };
  2008. PLDAPControl server_controls[2] =
  2009. {
  2010. &se_info_control,
  2011. NULL
  2012. };
  2013. pwszAttrArray[0] = pwszSDAttr;
  2014. pwszAttrArray[1] = L"name";
  2015. pwszAttrArray[2] = NULL;
  2016. ZeroMemory(&mapping, sizeof(mapping));
  2017. fCanInstall = false;
  2018. // Get the access token for current thread
  2019. hThread = GetCurrentThread();
  2020. if (NULL == hThread)
  2021. {
  2022. hr = myHLastError();
  2023. _JumpIfError(hr, error, "GetCurrentThread");
  2024. }
  2025. if (!OpenThreadToken(
  2026. hThread,
  2027. TOKEN_QUERY | TOKEN_DUPLICATE,
  2028. FALSE,
  2029. &hAccessToken))
  2030. {
  2031. hr = myHLastError();
  2032. if(hr==HRESULT_FROM_WIN32(ERROR_NO_TOKEN))
  2033. {
  2034. HANDLE hProcess = GetCurrentProcess();
  2035. if (NULL == hProcess)
  2036. {
  2037. hr = myHLastError();
  2038. _JumpError(hr, error, "GetCurrentProcess");
  2039. }
  2040. if (!OpenProcessToken(hProcess,
  2041. TOKEN_DUPLICATE,
  2042. &hAccessToken))
  2043. {
  2044. hr = myHLastError();
  2045. _JumpError(hr, error, "OpenProcessToken");
  2046. }
  2047. if (!DuplicateToken(hAccessToken, SecurityIdentification, &hDupToken))
  2048. {
  2049. hr = myHLastError();
  2050. _JumpError(hr, error, "DuplicateToken");
  2051. }
  2052. }
  2053. else
  2054. {
  2055. _JumpError(hr, error, "OpenThreadToken");
  2056. }
  2057. }
  2058. hr = myLdapOpen(
  2059. NULL, // pwszDomainName
  2060. RLBF_REQUIRE_GC | RLBF_REQUIRE_SECURE_LDAP, // dwFlags
  2061. &pld,
  2062. NULL, // pstrDomainDN
  2063. &bstrConfigDN);
  2064. _JumpIfError(hr, error, "myLdapOpen");
  2065. timeout.tv_sec = csecLDAPTIMEOUT;
  2066. timeout.tv_usec = 0;
  2067. hr = ldap_search_ext_s(
  2068. pld,
  2069. bstrConfigDN,
  2070. LDAP_SCOPE_SUBTREE,
  2071. pwszPKIContainerFilter,
  2072. pwszAttrArray,
  2073. 0,
  2074. (PLDAPControl *) server_controls,
  2075. NULL,
  2076. &timeout,
  2077. 0,
  2078. &pResult);
  2079. hr = myHLdapError(pld, hr, NULL);
  2080. _JumpIfError(hr, error, "ldap_search_ext_s");
  2081. pEntry = ldap_first_entry(pld, pResult);
  2082. if (NULL == pEntry)
  2083. {
  2084. hr = myHLdapLastError(pld, NULL);
  2085. _JumpError(hr, error, "ldap_first_entry");
  2086. }
  2087. bervalSD = ldap_get_values_len(pld, pEntry, pwszSDAttr);
  2088. if(bervalSD && (*bervalSD)->bv_val)
  2089. {
  2090. pSD = (*bervalSD)->bv_val;
  2091. if(IsValidSecurityDescriptor(pSD))
  2092. {
  2093. if(!AccessCheck(
  2094. pSD,
  2095. hDupToken,
  2096. ACTRL_DS_WRITE_PROP |
  2097. WRITE_DAC |
  2098. ACTRL_DS_CREATE_CHILD,
  2099. &mapping,
  2100. &PrivilegeSet,
  2101. &cPrivilegeSet,
  2102. &dwGrantedAccess,
  2103. &fAccess))
  2104. {
  2105. hr = myHLastError();
  2106. if(E_ACCESSDENIED==hr)
  2107. {
  2108. hr = S_OK;
  2109. }
  2110. _JumpError(hr, error, "AccessCheck");
  2111. }
  2112. }
  2113. else
  2114. {
  2115. DBGPRINT((DBG_SS_CERTOCM, "Invalid security descriptor for PKI container" ));
  2116. }
  2117. }
  2118. else
  2119. {
  2120. DBGPRINT((DBG_SS_CERTOCM, "No security descriptor for PKI container" ));
  2121. }
  2122. if(fAccess)
  2123. {
  2124. fCanInstall = true;
  2125. }
  2126. error:
  2127. if(bervalSD)
  2128. {
  2129. ldap_value_free_len(bervalSD);
  2130. }
  2131. if (NULL != pResult)
  2132. {
  2133. ldap_msgfree(pResult);
  2134. }
  2135. myLdapClose(pld, NULL, bstrConfigDN);
  2136. if (hAccessToken)
  2137. {
  2138. CloseHandle(hAccessToken);
  2139. }
  2140. if (hDupToken)
  2141. {
  2142. CloseHandle(hDupToken);
  2143. }
  2144. //we should always return S_OK; since we do not want to abort
  2145. //ocmsetup just because we failed to contact the directory
  2146. return S_OK;
  2147. }
  2148. HRESULT myLdapFindObjectInForest(
  2149. IN LDAP *pld,
  2150. IN LPCWSTR pwszFilter,
  2151. OUT LPWSTR *ppwszURL)
  2152. {
  2153. HRESULT hr;
  2154. LPWSTR pwszAttrArray[2] = {wszDSDNATTRIBUTE, NULL};
  2155. LDAPMessage* pResult = NULL;
  2156. LDAPMessage *pEntry;
  2157. LPWSTR *pwszValue = NULL;
  2158. hr = ldap_search_s(
  2159. pld,
  2160. NULL,
  2161. LDAP_SCOPE_SUBTREE,
  2162. const_cast<WCHAR*>(pwszFilter),
  2163. pwszAttrArray,
  2164. 0,
  2165. &pResult);
  2166. hr = myHLdapError(pld, hr, NULL);
  2167. _JumpIfError(hr, error, "ldap_search_s");
  2168. pEntry = ldap_first_entry(pld, pResult);
  2169. if (NULL == pEntry)
  2170. {
  2171. hr = myHLdapLastError(pld, NULL);
  2172. _JumpError(hr, error, "ldap_first_entry");
  2173. }
  2174. pwszValue = ldap_get_values(pld, pEntry, wszDSDNATTRIBUTE);
  2175. if(pwszValue && pwszValue[0])
  2176. {
  2177. hr = myDupString(pwszValue[0], ppwszURL);
  2178. _JumpIfError(hr, error, "myDupString");
  2179. }
  2180. error:
  2181. if(pwszValue)
  2182. {
  2183. ldap_value_free(pwszValue);
  2184. }
  2185. if (NULL != pResult)
  2186. {
  2187. ldap_msgfree(pResult);
  2188. }
  2189. return hr;
  2190. }
  2191. HRESULT ExtractMachineNameFromDNSName(
  2192. LPCWSTR pcwszDNS,
  2193. LPWSTR *ppcwszMachineName)
  2194. {
  2195. HRESULT hr;
  2196. WCHAR *pwszDot = wcschr(pcwszDNS, L'.');
  2197. DWORD nLen;
  2198. nLen = (pwszDot?
  2199. SAFE_SUBTRACT_POINTERS(pwszDot, pcwszDNS):
  2200. wcslen(pcwszDNS))+1;
  2201. *ppcwszMachineName = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*nLen);
  2202. _JumpIfAllocFailed(*ppcwszMachineName, error);
  2203. wcsncpy(*ppcwszMachineName, pcwszDNS, nLen);
  2204. (*ppcwszMachineName)[nLen-1] = L'\0';
  2205. hr = S_OK;
  2206. error:
  2207. return hr;
  2208. }
  2209. HRESULT myLdapFindComputerInForest(
  2210. IN LDAP *pld,
  2211. IN LPCWSTR pwszMachineDNS,
  2212. OUT LPWSTR *ppwszURL)
  2213. {
  2214. HRESULT hr;
  2215. LPWSTR pwszAttrArray[] = {
  2216. wszDSDNATTRIBUTE,
  2217. wszDSNAMEATTRIBUTE,
  2218. wszDSDNSHOSTNAMEATTRIBUTE,
  2219. NULL};
  2220. LDAPMessage* pResult = NULL;
  2221. LDAPMessage *pEntry;
  2222. LPWSTR *pwszValue = NULL;
  2223. LPWSTR *pwszName = NULL;
  2224. LPWSTR *pwszDNSHostName = NULL;
  2225. LPWSTR pwszFilterFormat1 = L"(&(objectCategory=computer)(name=%s))";
  2226. LPWSTR pwszFilterFormat2 = L"(&(objectCategory=computer)(dNSHostName=%s))";
  2227. LPWSTR pwszFilter = NULL;
  2228. LPWSTR pwszMachineName = NULL;
  2229. bool fMachineNameIsInDNSFormat;
  2230. // First, try to find the machine based on the DNS name prefix, which usually
  2231. // matches the computer object name
  2232. hr = ExtractMachineNameFromDNSName(
  2233. pwszMachineDNS,
  2234. &pwszMachineName);
  2235. _JumpIfError(hr, error, "ExtractMachineNameFromDNSName");
  2236. // if extracted name and dns name don't match, then we were called
  2237. // with a DNS name
  2238. fMachineNameIsInDNSFormat = (0!=wcscmp(pwszMachineDNS, pwszMachineName));
  2239. pwszFilter = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*
  2240. (wcslen(pwszFilterFormat1)+wcslen(pwszMachineName)+1));
  2241. _JumpIfAllocFailed(pwszFilter, error);
  2242. wsprintf(pwszFilter, pwszFilterFormat1, pwszMachineName);
  2243. hr = ldap_search_s(
  2244. pld,
  2245. NULL,
  2246. LDAP_SCOPE_SUBTREE,
  2247. pwszFilter,
  2248. pwszAttrArray,
  2249. 0,
  2250. &pResult);
  2251. hr = myHLdapError(pld, hr, NULL);
  2252. _JumpIfError(hr, error, "ldap_search_s");
  2253. pEntry = ldap_first_entry(pld, pResult);
  2254. if (NULL == pEntry)
  2255. {
  2256. hr = CRYPT_E_NOT_FOUND;
  2257. _JumpError(hr, error, "ldap_first_entry");
  2258. }
  2259. pwszName = ldap_get_values(pld, pEntry, wszDSNAMEATTRIBUTE);
  2260. if(pwszName && pwszName[0])
  2261. {
  2262. // found a matching object, but do DNS name match?
  2263. pwszDNSHostName = ldap_get_values(pld, pEntry, wszDSDNSHOSTNAMEATTRIBUTE);
  2264. if(fMachineNameIsInDNSFormat &&
  2265. pwszDNSHostName &&
  2266. pwszDNSHostName[0] &&
  2267. 0 != _wcsicmp(pwszDNSHostName[0], pwszMachineDNS))
  2268. {
  2269. // Couldn't find a computer object matching the DNS prefix, try searching
  2270. // on dNSHostName. This attribute is not indexed so the searching will
  2271. // be very slow
  2272. LocalFree(pwszFilter);
  2273. pwszFilter = NULL;
  2274. ldap_msgfree(pResult);
  2275. pResult = NULL;
  2276. pEntry = NULL;
  2277. pwszFilter = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*
  2278. (wcslen(pwszFilterFormat2)+wcslen(pwszMachineDNS)+1));
  2279. _JumpIfAllocFailed(pwszFilter, error);
  2280. wsprintf(pwszFilter, pwszFilterFormat2, pwszMachineDNS);
  2281. hr = ldap_search_s(
  2282. pld,
  2283. NULL,
  2284. LDAP_SCOPE_SUBTREE,
  2285. pwszFilter,
  2286. pwszAttrArray,
  2287. 0,
  2288. &pResult);
  2289. hr = myHLdapError(pld, hr, NULL);
  2290. _JumpIfError(hr, error, "ldap_search_s");
  2291. pEntry = ldap_first_entry(pld, pResult);
  2292. if (NULL == pEntry)
  2293. {
  2294. hr = CRYPT_E_NOT_FOUND;
  2295. _JumpError(hr, error, "ldap_first_entry");
  2296. }
  2297. }
  2298. }
  2299. pwszValue = ldap_get_values(pld, pEntry, wszDSDNATTRIBUTE);
  2300. if(pwszValue)
  2301. {
  2302. hr = myDupString(pwszValue[0], ppwszURL);
  2303. _JumpIfError(hr, error, "myDupString");
  2304. }
  2305. error:
  2306. if(pwszValue)
  2307. {
  2308. ldap_value_free(pwszValue);
  2309. }
  2310. if(pwszName)
  2311. {
  2312. ldap_value_free(pwszName);
  2313. }
  2314. if(pwszMachineName)
  2315. {
  2316. LocalFree(pwszMachineName);
  2317. }
  2318. if(pwszFilter)
  2319. {
  2320. LocalFree(pwszFilter);
  2321. }
  2322. if (NULL != pResult)
  2323. {
  2324. ldap_msgfree(pResult);
  2325. }
  2326. return hr;
  2327. }
  2328. ///////////////////////////////////////////////////////////////////////////////
  2329. // The following code loads a list of certificates from a DS object (eg
  2330. // AIA CACertificate property), filters out unwanted certs and writes it
  2331. // back to DS.
  2332. //
  2333. // Certs are loaded into a data structure that looks like this:
  2334. //
  2335. // CFilteredCertList
  2336. // |
  2337. // CCertBucket1->CCertBucket2->...->CertBucketn
  2338. // | |
  2339. // CCertItem1->CertItem2->... CCertItem1->CCertItem2->...
  2340. //
  2341. // Each cert bucket has a list of certs that match some criteria, in our
  2342. // case they share the same subject and public key.
  2343. //
  2344. // After filtering, the buckets in the list must contain:
  2345. //
  2346. // if only expired certs were found with this subject&key
  2347. // keep the most recent expired cert
  2348. // else
  2349. // keep all valid certs only
  2350. //
  2351. // For that, we process one cert context at a time. The filtering algorithm is:
  2352. //
  2353. // if no matching bucket (same subj & key) found
  2354. // create a new bucket it
  2355. // else
  2356. // if cert is expired
  2357. // if bucket contains only expired certs and
  2358. // this cert is newer
  2359. // replace cert in bucket
  2360. // else
  2361. // if bucket contains expired certs
  2362. // replace cert in bucket
  2363. // else
  2364. // add cert to bucket
  2365. ///////////////////////////////////////////////////////////////////////////////
  2366. // CCertItem: wrapper for one certificate context
  2367. class CCertItem
  2368. {
  2369. public:
  2370. CCertItem(PCCERT_CONTEXT pcc) :
  2371. m_pcc(pcc), m_fExpired(false) {}
  2372. ~CCertItem()
  2373. {
  2374. CleanupCertContext();
  2375. }
  2376. void SetExpired(bool fExpired) { m_fExpired = fExpired; }
  2377. void SetCertContext(PCCERT_CONTEXT pccNew)
  2378. {
  2379. CleanupCertContext();
  2380. m_pcc = pccNew;
  2381. }
  2382. PCCERT_CONTEXT GetCertContext() { return m_pcc; }
  2383. bool IsExpired() { return m_fExpired; }
  2384. private:
  2385. void CleanupCertContext()
  2386. {
  2387. if(m_pcc)
  2388. CertFreeCertificateContext(m_pcc);
  2389. }
  2390. PCCERT_CONTEXT m_pcc;
  2391. bool m_fExpired;
  2392. };
  2393. typedef TPtrList<CCertItem> CERTITEMLIST;
  2394. typedef TPtrListEnum<CCertItem> CERTITEMLISTENUM;
  2395. ///////////////////////////////////////////////////////////////////////////////
  2396. // CCertBucket: bucket of certificates with same subject and publick key
  2397. class CCertBucket
  2398. {
  2399. public:
  2400. CCertItem *GetFirstCert()
  2401. {
  2402. return m_CertList.GetAt(0);
  2403. }
  2404. bool AddToBucket(CCertItem *pCertItem)
  2405. {
  2406. return m_CertList.AddHead(pCertItem);
  2407. }
  2408. bool CCertBucket::ReplaceBucket(CCertItem *pCertItem)
  2409. {
  2410. m_CertList.Cleanup();
  2411. return m_CertList.AddHead(pCertItem);
  2412. }
  2413. bool InitBucket(CCertItem *pCertItem)
  2414. {
  2415. return m_CertList.AddHead(pCertItem);
  2416. }
  2417. friend class CFilteredCertList;
  2418. private:
  2419. CERTITEMLIST m_CertList;
  2420. };
  2421. typedef TPtrList<CCertBucket> CERTBUCKETLIST;
  2422. typedef TPtrListEnum<CCertBucket> CERTBUCKETLISTENUM;
  2423. ///////////////////////////////////////////////////////////////////////////////
  2424. // CFilteredCertList: list of certificate buckets (one bucket contains certs
  2425. // with same subject and public key). Upon insertion we follow the algorithm
  2426. // described above.
  2427. //
  2428. // To change the filtering behavior, derive from this class and override
  2429. // InsertCert method.
  2430. class CFilteredCertList
  2431. {
  2432. public:
  2433. CFilteredCertList() {};
  2434. ~CFilteredCertList() {};
  2435. bool InsertCert(CCertItem *pCertItem);
  2436. int GetCount();
  2437. HRESULT ImportFromBervals(struct berval **pBervals);
  2438. HRESULT ExportToBervals(struct berval **&pBervals);
  2439. protected:
  2440. CCertBucket * FindBucket(CCertItem *pCertItem);
  2441. bool AddNewBucket(CCertItem *pCertItem);
  2442. bool BelongsToBucket(CCertBucket *pCertBucket, CCertItem *pCertItem);
  2443. bool ReplaceBucket(CCertItem *pCertItem);
  2444. bool InsertCertInBucket(CCertBucket *pCertBucket, CCertItem *pCertItem);
  2445. private:
  2446. CERTBUCKETLIST m_BucketList;
  2447. };
  2448. ///////////////////////////////////////////////////////////////////////////////
  2449. // CFilteredCertList methods
  2450. int CFilteredCertList::GetCount()
  2451. {
  2452. int nCount = 0;
  2453. CERTBUCKETLISTENUM BucketListEnum(m_BucketList);
  2454. CCertBucket * pBucket;
  2455. for(pBucket = BucketListEnum.Next();
  2456. pBucket;
  2457. pBucket = BucketListEnum.Next())
  2458. {
  2459. nCount += pBucket->m_CertList.GetCount();
  2460. }
  2461. return nCount;
  2462. }
  2463. bool CFilteredCertList::BelongsToBucket(
  2464. CCertBucket *pCertBucket,
  2465. CCertItem *pCertItem)
  2466. {
  2467. PCCERT_CONTEXT pCertContext1 =
  2468. pCertBucket->GetFirstCert()->GetCertContext();
  2469. PCCERT_CONTEXT pCertContext2 =
  2470. pCertItem->GetCertContext();
  2471. // belongs to this bucket if subject and public key match
  2472. return
  2473. (0 == memcmp(
  2474. pCertContext1->pCertInfo->Subject.pbData,
  2475. pCertContext2->pCertInfo->Subject.pbData,
  2476. pCertContext1->pCertInfo->Subject.cbData)) &&
  2477. (0 == memcmp(
  2478. pCertContext1->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
  2479. pCertContext2->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
  2480. pCertContext1->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData));
  2481. }
  2482. ///////////////////////////////////////////////////////////////////////////////
  2483. CCertBucket *CFilteredCertList::FindBucket(CCertItem *pCertItem)
  2484. {
  2485. CERTBUCKETLISTENUM BucketListEnum(m_BucketList);
  2486. CCertBucket * pBucket;
  2487. for(pBucket = BucketListEnum.Next();
  2488. pBucket;
  2489. pBucket = BucketListEnum.Next())
  2490. {
  2491. if(BelongsToBucket(pBucket, pCertItem))
  2492. return pBucket;
  2493. }
  2494. return NULL;
  2495. }
  2496. ///////////////////////////////////////////////////////////////////////////////
  2497. bool CFilteredCertList::AddNewBucket(CCertItem *pCertItem)
  2498. {
  2499. CCertBucket *pBucket = new CCertBucket();
  2500. if(!pBucket->InitBucket(pCertItem))
  2501. return false;
  2502. if(m_BucketList.AddHead(pBucket))
  2503. {
  2504. return true;
  2505. }
  2506. else
  2507. {
  2508. return false;
  2509. }
  2510. }
  2511. ///////////////////////////////////////////////////////////////////////////////
  2512. bool CFilteredCertList::InsertCertInBucket(
  2513. CCertBucket *pCertBucket,
  2514. CCertItem *pCertItem)
  2515. {
  2516. bool fRet = false;
  2517. CCertItem * pFirstCert = pCertBucket->GetFirstCert();
  2518. // if cert is expired
  2519. if(pCertItem->IsExpired())
  2520. {
  2521. // if bucket contains only expired certs and
  2522. // this cert is newer
  2523. if(pFirstCert->IsExpired() &&
  2524. 0 < CompareFileTime(
  2525. &(pCertItem->GetCertContext()->pCertInfo->NotAfter),
  2526. &(pFirstCert->GetCertContext()->pCertInfo->NotAfter)))
  2527. {
  2528. // replace cert in bucket
  2529. fRet = pCertBucket->ReplaceBucket(pCertItem);
  2530. }
  2531. }
  2532. else
  2533. {
  2534. // if bucket contains expired certs
  2535. if(pFirstCert->IsExpired())
  2536. {
  2537. // replace cert in bucket
  2538. fRet = pCertBucket->ReplaceBucket(pCertItem);
  2539. }
  2540. else
  2541. {
  2542. // add cert to bucket
  2543. fRet = pCertBucket->AddToBucket(pCertItem);
  2544. }
  2545. }
  2546. return fRet;
  2547. }
  2548. bool CFilteredCertList::InsertCert(CCertItem *pCertItem)
  2549. {
  2550. CCertBucket *pBucket;
  2551. pBucket = FindBucket(pCertItem);
  2552. // if no matching bucket (same subj & key) found
  2553. // create a new bucket it
  2554. if(!pBucket)
  2555. {
  2556. return AddNewBucket(pCertItem);
  2557. }
  2558. else
  2559. {
  2560. return InsertCertInBucket(pBucket, pCertItem);
  2561. }
  2562. }
  2563. ///////////////////////////////////////////////////////////////////////////////
  2564. // Loads cert contexts from LDAP structure, an array of pointers to
  2565. // berval structs which hold cert blobs.
  2566. HRESULT
  2567. CFilteredCertList::ImportFromBervals(
  2568. struct berval **pBervals)
  2569. {
  2570. HRESULT hr;
  2571. PCCERT_CONTEXT pcc;
  2572. int i;
  2573. FILETIME ft;
  2574. // Consider old certs that are one minute old
  2575. GetSystemTimeAsFileTime(&ft);
  2576. myMakeExprDateTime(&ft, -1, ENUM_PERIOD_MINUTES);
  2577. for (i = 0; NULL != pBervals[i]; i++)
  2578. {
  2579. struct berval *pberval = pBervals[i];
  2580. pcc = CertCreateCertificateContext(
  2581. X509_ASN_ENCODING,
  2582. (BYTE *) pberval->bv_val,
  2583. pberval->bv_len);
  2584. if (NULL == pcc)
  2585. {
  2586. // not a valid cert, ignore
  2587. _PrintError(myHLastError(), "CreateCertificateContext");
  2588. continue;
  2589. }
  2590. CCertItem * pci = new CCertItem(pcc); // CCertItem takes ownership
  2591. _JumpIfAllocFailed(pci, error); // of this cert context and will
  2592. // CertFreeCertificateContext in
  2593. // destructor
  2594. pci->SetExpired(
  2595. 0 > CompareFileTime(&pcc->pCertInfo->NotAfter, &ft));
  2596. if(!InsertCert(pci)) // InsertCert returns true if cert
  2597. { // was added to the list, in which case
  2598. delete pcc; // the list destructor will cleanup.
  2599. } // If not, we need to delete explicitely
  2600. }
  2601. hr = S_OK;
  2602. error:
  2603. return hr;
  2604. }
  2605. ///////////////////////////////////////////////////////////////////////////////
  2606. // Builds an LDAP structure from the list of certs, to be written back to DS.
  2607. // LDAP struct is an array of pointers to struct bervals structures, terminated
  2608. // with a NULL pointer. We allocate the pointer array and the space for berval
  2609. // structs in one call.
  2610. // Caller is responsible for LocalFree'ing pBervals.
  2611. HRESULT CFilteredCertList::ExportToBervals(struct berval **&pBervals)
  2612. {
  2613. HRESULT hr;
  2614. CERTBUCKETLISTENUM BucketListEnum(m_BucketList);
  2615. CCertBucket *pBucket;
  2616. int i;
  2617. DWORD dwSize;
  2618. struct berval *pBervalData;
  2619. // total size of pointers array plus size of array of berval structs
  2620. dwSize = (GetCount()+1) * sizeof(pBervals[0]) +
  2621. GetCount() * sizeof(struct berval);
  2622. pBervals = (struct berval **) LocalAlloc(LMEM_FIXED, dwSize);
  2623. _JumpIfAllocFailed(pBervals, error);
  2624. // starting address for the berval arrays
  2625. pBervalData = (struct berval *)(pBervals+GetCount()+1);
  2626. for(i=0, pBucket = BucketListEnum.Next();
  2627. pBucket;
  2628. pBucket = BucketListEnum.Next())
  2629. {
  2630. CERTITEMLISTENUM CertListEnum(pBucket->m_CertList);
  2631. CCertItem *pCertItem;
  2632. for(pCertItem = CertListEnum.Next();
  2633. pCertItem;
  2634. i++, pCertItem = CertListEnum.Next())
  2635. {
  2636. // set the pointer to the associated berval struct
  2637. pBervals[i] = pBervalData+i;
  2638. // init the berval struct
  2639. pBervalData[i].bv_val = (char *)
  2640. pCertItem->GetCertContext()->pbCertEncoded;
  2641. pBervalData[i].bv_len = pCertItem->GetCertContext()->cbCertEncoded;
  2642. }
  2643. }
  2644. pBervals[i] = NULL;
  2645. hr = S_OK;
  2646. error:
  2647. return hr;
  2648. }
  2649. ///////////////////////////////////////////////////////////////////////////////
  2650. // Loads the certificate blobs stored in the specified object&property, filters
  2651. // them and writes them back to DS.
  2652. // Filtering keeps all valid certificates and no expired certs. If only expired
  2653. // have been found, it keeps the most recent one.
  2654. HRESULT
  2655. myLdapFilterCertificates(
  2656. IN LDAP *pld,
  2657. IN LPCWSTR pcwszDN,
  2658. IN LPCWSTR pcwszAttribute,
  2659. OUT DWORD *pdwDisposition,
  2660. OPTIONAL OUT WCHAR **ppwszError)
  2661. {
  2662. HRESULT hr;
  2663. DWORD cres;
  2664. LONG cber;
  2665. DWORD i;
  2666. LDAP_TIMEVAL timeval;
  2667. LDAPMessage *pmsg = NULL;
  2668. LDAPMessage *pres;
  2669. WCHAR *apwszAttrs[2];
  2670. struct berval **ppberval = NULL;
  2671. struct berval **prgpberVals = NULL;
  2672. CFilteredCertList NewCertList;
  2673. CAutoLPWSTR strDN;
  2674. LPWSTR pcwszSuffix; // no free
  2675. *pdwDisposition = LDAP_OTHER;
  2676. if (NULL != ppwszError)
  2677. {
  2678. *ppwszError = NULL;
  2679. }
  2680. hr = TrimURLDN(pcwszDN, &pcwszSuffix, &strDN);
  2681. _JumpIfError(hr, error, "TrimURLDN");
  2682. apwszAttrs[0] = const_cast<WCHAR *>(pcwszAttribute);
  2683. apwszAttrs[1] = NULL;
  2684. timeval.tv_sec = csecLDAPTIMEOUT;
  2685. timeval.tv_usec = 0;
  2686. hr = ldap_search_st(
  2687. pld, // ld
  2688. strDN, // base
  2689. LDAP_SCOPE_BASE, // scope
  2690. NULL, // filter
  2691. apwszAttrs, // attrs
  2692. FALSE, // attrsonly
  2693. &timeval, // timeout
  2694. &pmsg); // res
  2695. if (S_OK != hr)
  2696. {
  2697. *pdwDisposition = hr;
  2698. hr = myHLdapError(pld, hr, NULL);
  2699. _JumpErrorStr(hr, error, "ldap_search_st", pcwszDN);
  2700. }
  2701. cres = ldap_count_entries(pld, pmsg);
  2702. if (0 == cres)
  2703. {
  2704. // No entries were found.
  2705. hr = NTE_NOT_FOUND;
  2706. _JumpError(hr, error, "ldap_count_entries");
  2707. }
  2708. pres = ldap_first_entry(pld, pmsg);
  2709. if (NULL == pres)
  2710. {
  2711. hr = NTE_NOT_FOUND;
  2712. _JumpError(hr, error, "ldap_first_entry");
  2713. }
  2714. ppberval = ldap_get_values_len(
  2715. pld,
  2716. pres,
  2717. const_cast<WCHAR *>(pcwszAttribute));
  2718. if (NULL != ppberval)
  2719. {
  2720. // count entries
  2721. cber = 0;
  2722. for (i = 0; NULL != ppberval[i]; i++, cber++)
  2723. NULL;
  2724. // load and filter certs
  2725. hr = NewCertList.ImportFromBervals(ppberval);
  2726. _JumpIfError(hr, error, "ImportFromBervals");
  2727. // if number of certs is the same, no need to write it back
  2728. // (order doesn't matter)
  2729. if (cber != NewCertList.GetCount())
  2730. {
  2731. // walk the list and copy the cert blobs
  2732. hr = NewCertList.ExportToBervals(prgpberVals);
  2733. _JumpIfError(hr, error, "ExportToBervals");
  2734. // set disposition assuming there's nothing to do:
  2735. *pdwDisposition = LDAP_ATTRIBUTE_OR_VALUE_EXISTS;
  2736. LDAPMod *mods[2];
  2737. LDAPMod certmod;
  2738. mods[0] = &certmod;
  2739. mods[1] = NULL;
  2740. certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  2741. certmod.mod_type = const_cast<WCHAR *>(pcwszAttribute);
  2742. certmod.mod_bvalues = prgpberVals;
  2743. hr = ldap_modify_ext_s(
  2744. pld,
  2745. strDN,
  2746. mods,
  2747. NULL,
  2748. NULL);
  2749. *pdwDisposition = hr;
  2750. if (hr != S_OK)
  2751. {
  2752. hr = myHLdapError(pld, hr, ppwszError);
  2753. _JumpError(hr, error, "ldap_modify_ext_s");
  2754. }
  2755. }
  2756. }
  2757. hr = S_OK;
  2758. error:
  2759. if (NULL != prgpberVals)
  2760. {
  2761. LocalFree(prgpberVals);
  2762. }
  2763. if (NULL != ppberval)
  2764. {
  2765. ldap_value_free_len(ppberval);
  2766. }
  2767. if (NULL != pmsg)
  2768. {
  2769. ldap_msgfree(pmsg);
  2770. }
  2771. return(hr);
  2772. }