Source code of Windows XP (NT5)
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.

2059 lines
45 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. static CHAR s_sdBerValue[] = {
  21. BER_SEQUENCE,
  22. 3 * sizeof(BYTE), // three byte sequence
  23. BER_INTEGER,
  24. 1 * sizeof(BYTE), // of one-byte integer
  25. DACL_SECURITY_INFORMATION
  26. //OWNER_SECURITY_INFORMATION |
  27. //GROUP_SECURITY_INFORMATION
  28. };
  29. static LDAPControl s_se_info_control =
  30. {
  31. LDAP_SERVER_SD_FLAGS_OID_W,
  32. { ARRAYSIZE(s_sdBerValue), s_sdBerValue },
  33. TRUE
  34. };
  35. LDAPControl *g_rgLdapControls[2] = { &s_se_info_control, NULL };
  36. // Revocation templates
  37. WCHAR const g_wszHTTPRevocationURLTemplate[] = // Fetch CRL via http:
  38. L"http://"
  39. wszFCSAPARM_SERVERDNSNAME
  40. L"/CertEnroll/"
  41. wszFCSAPARM_SANITIZEDCANAME
  42. wszFCSAPARM_CRLFILENAMESUFFIX
  43. wszFCSAPARM_CRLDELTAFILENAMESUFFIX
  44. L".crl";
  45. WCHAR const g_wszFILERevocationURLTemplate[] = // Fetch CRL via file:
  46. L"file://\\\\"
  47. wszFCSAPARM_SERVERDNSNAME
  48. L"\\CertEnroll\\"
  49. wszFCSAPARM_SANITIZEDCANAME
  50. wszFCSAPARM_CRLFILENAMESUFFIX
  51. wszFCSAPARM_CRLDELTAFILENAMESUFFIX
  52. L".crl";
  53. WCHAR const g_wszASPRevocationURLTemplate[] = // ASP revocation check via https:
  54. L"https://"
  55. wszFCSAPARM_SERVERDNSNAME
  56. L"/CertEnroll/nsrev_"
  57. wszFCSAPARM_SANITIZEDCANAME
  58. L".asp";
  59. #define wszCDPDNTEMPLATE \
  60. L"CN=" \
  61. wszFCSAPARM_SANITIZEDCANAMEHASH \
  62. wszFCSAPARM_CRLFILENAMESUFFIX \
  63. L"," \
  64. L"CN=" \
  65. wszFCSAPARM_SERVERSHORTNAME \
  66. L"," \
  67. L"CN=CDP," \
  68. L"CN=Public Key Services," \
  69. L"CN=Services," \
  70. wszFCSAPARM_CONFIGDN
  71. WCHAR const g_wszzLDAPRevocationURLTemplate[] = // Fetch CRL via ldap:
  72. L"ldap:///"
  73. wszCDPDNTEMPLATE
  74. wszFCSAPARM_DSCRLATTRIBUTE
  75. L"\0";
  76. // Publish CRL via ldap:
  77. WCHAR const g_wszCDPDNTemplate[] = wszCDPDNTEMPLATE;
  78. // AIA templates
  79. WCHAR const g_wszHTTPIssuerCertURLTemplate[] = // Fetch CA Cert via http:
  80. L"http://"
  81. wszFCSAPARM_SERVERDNSNAME
  82. L"/CertEnroll/"
  83. wszFCSAPARM_SERVERDNSNAME
  84. L"_"
  85. wszFCSAPARM_SANITIZEDCANAME
  86. wszFCSAPARM_CERTFILENAMESUFFIX
  87. L".crt"
  88. L"\0";
  89. WCHAR const g_wszFILEIssuerCertURLTemplate[] = // Fetch CA Cert via http:
  90. L"file://\\\\"
  91. wszFCSAPARM_SERVERDNSNAME
  92. L"\\CertEnroll\\"
  93. wszFCSAPARM_SERVERDNSNAME
  94. L"_"
  95. wszFCSAPARM_SANITIZEDCANAME
  96. wszFCSAPARM_CERTFILENAMESUFFIX
  97. L".crt"
  98. L"\0";
  99. #define wszAIADNTEMPLATE \
  100. L"CN=" \
  101. wszFCSAPARM_SANITIZEDCANAMEHASH \
  102. L"," \
  103. L"CN=AIA," \
  104. L"CN=Public Key Services," \
  105. L"CN=Services," \
  106. wszFCSAPARM_CONFIGDN
  107. WCHAR const g_wszzLDAPIssuerCertURLTemplate[] = // Fetch CA Cert via ldap:
  108. L"ldap:///"
  109. wszAIADNTEMPLATE
  110. wszFCSAPARM_DSCACERTATTRIBUTE
  111. L"\0";
  112. // Publish CA Cert via ldap:
  113. WCHAR const g_wszAIADNTemplate[] = wszAIADNTEMPLATE;
  114. #define wszNTAUTHDNTEMPLATE \
  115. L"CN=NTAuthCertificates," \
  116. L"CN=Public Key Services," \
  117. L"CN=Services," \
  118. wszFCSAPARM_CONFIGDN
  119. WCHAR const g_wszLDAPNTAuthURLTemplate[] = // Fetch NTAuth Certs via ldap:
  120. L"ldap:///"
  121. wszNTAUTHDNTEMPLATE
  122. wszFCSAPARM_DSCACERTATTRIBUTE;
  123. #define wszROOTTRUSTDNTEMPLATE \
  124. L"CN=" \
  125. wszFCSAPARM_SANITIZEDCANAMEHASH \
  126. L"," \
  127. L"CN=Certification Authorities," \
  128. L"CN=Public Key Services," \
  129. L"CN=Services," \
  130. wszFCSAPARM_CONFIGDN
  131. WCHAR const g_wszLDAPRootTrustURLTemplate[] = // Fetch Root Certs via ldap:
  132. L"ldap:///"
  133. wszROOTTRUSTDNTEMPLATE
  134. wszFCSAPARM_DSCACERTATTRIBUTE;
  135. #define wszKRADNTEMPLATE \
  136. L"CN=" \
  137. wszFCSAPARM_SANITIZEDCANAMEHASH \
  138. L"," \
  139. L"CN=KRA," \
  140. L"CN=Public Key Services," \
  141. L"CN=Services," \
  142. wszFCSAPARM_CONFIGDN
  143. WCHAR const g_wszzLDAPKRACertURLTemplate[] = // Fetch KRA Cert via ldap:
  144. L"ldap:///"
  145. wszKRADNTEMPLATE
  146. wszFCSAPARM_DSKRACERTATTRIBUTE
  147. L"\0";
  148. // Publish KRA Certs via ldap:
  149. WCHAR const g_wszKRADNTemplate[] = wszKRADNTEMPLATE;
  150. //+--------------------------------------------------------------------------
  151. //
  152. // Routine Description:
  153. // This routine simply queries the operational attributes for the
  154. // domaindn and configdn.
  155. //
  156. // The strings returned by this routine must be freed by the caller
  157. // using SysFreeString
  158. //
  159. // Parameters:
  160. // pld -- a valid handle to an ldap session
  161. // pstrDomainDN -- a pointer to a string to be allocated in this routine
  162. // pstrConfigDN -- a pointer to a string to be allocated in this routine
  163. //
  164. // Return Values:
  165. // HRESULT for operation error.
  166. //
  167. //---------------------------------------------------------------------------
  168. HRESULT
  169. myGetAuthoritativeDomainDn(
  170. IN LDAP *pld,
  171. OPTIONAL OUT BSTR *pstrDomainDN,
  172. OPTIONAL OUT BSTR *pstrConfigDN)
  173. {
  174. HRESULT hr;
  175. LDAPMessage *pSearchResult = NULL;
  176. LDAPMessage *pEntry;
  177. WCHAR *pwszAttrName;
  178. BerElement *pber;
  179. WCHAR **rgpwszValues;
  180. WCHAR *apwszAttrArray[3];
  181. WCHAR *pwszDefaultNamingContext = L"defaultNamingContext";
  182. WCHAR *pwszConfigurationNamingContext = L"configurationNamingContext";
  183. WCHAR *pwszObjectClassFilter = L"objectClass=*";
  184. BSTR strDomainDN = NULL;
  185. BSTR strConfigDN = NULL;
  186. // Set the OUT parameters to NULL
  187. if (NULL != pstrConfigDN)
  188. {
  189. *pstrConfigDN = NULL;
  190. }
  191. if (NULL != pstrDomainDN)
  192. {
  193. *pstrDomainDN = NULL;
  194. }
  195. // Query for the ldap server oerational attributes to obtain the default
  196. // naming context.
  197. apwszAttrArray[0] = pwszDefaultNamingContext;
  198. apwszAttrArray[1] = pwszConfigurationNamingContext;
  199. apwszAttrArray[2] = NULL; // this is the sentinel
  200. hr = ldap_search_s(
  201. pld,
  202. NULL,
  203. LDAP_SCOPE_BASE,
  204. pwszObjectClassFilter,
  205. apwszAttrArray,
  206. FALSE,
  207. &pSearchResult);
  208. hr = myHLdapError(pld, hr, NULL);
  209. _JumpIfError(hr, error, "ldap_search_sW");
  210. pEntry = ldap_first_entry(pld, pSearchResult);
  211. if (NULL == pEntry)
  212. {
  213. hr = myHLdapLastError(pld, NULL);
  214. _JumpError(hr, error, "ldap_first_entry");
  215. }
  216. pwszAttrName = ldap_first_attribute(pld, pEntry, &pber);
  217. while (NULL != pwszAttrName)
  218. {
  219. BSTR *pstr = NULL;
  220. if (NULL != pstrDomainDN &&
  221. 0 == lstrcmpi(pwszAttrName, pwszDefaultNamingContext))
  222. {
  223. pstr = &strDomainDN;
  224. }
  225. else
  226. if (NULL != pstrConfigDN &&
  227. 0 == lstrcmpi(pwszAttrName, pwszConfigurationNamingContext))
  228. {
  229. pstr = &strConfigDN;
  230. }
  231. if (NULL != pstr && NULL == *pstr)
  232. {
  233. rgpwszValues = ldap_get_values(pld, pEntry, pwszAttrName);
  234. if (NULL != rgpwszValues)
  235. {
  236. if (NULL != rgpwszValues[0])
  237. {
  238. *pstr = SysAllocString(rgpwszValues[0]);
  239. if (NULL == *pstr)
  240. {
  241. hr = E_OUTOFMEMORY;
  242. _JumpError(hr, error, "SysAllocString");
  243. }
  244. }
  245. ldap_value_free(rgpwszValues);
  246. }
  247. }
  248. ldap_memfree(pwszAttrName);
  249. pwszAttrName = ldap_next_attribute(pld, pEntry, pber);
  250. }
  251. if ((NULL != pstrDomainDN && NULL == strDomainDN) ||
  252. (NULL != pstrConfigDN && NULL == strConfigDN))
  253. {
  254. // We couldn't get default domain info - bail out
  255. hr = HRESULT_FROM_WIN32(ERROR_CANT_ACCESS_DOMAIN_INFO);
  256. _JumpError(hr, error, "missing domain info");
  257. }
  258. if (NULL != pstrDomainDN)
  259. {
  260. *pstrDomainDN = strDomainDN;
  261. strDomainDN = NULL;
  262. }
  263. if (NULL != pstrConfigDN)
  264. {
  265. *pstrConfigDN = strConfigDN;
  266. strConfigDN = NULL;
  267. }
  268. hr = S_OK;
  269. error:
  270. if (NULL != pSearchResult)
  271. {
  272. ldap_msgfree(pSearchResult);
  273. }
  274. myLdapClose(NULL, strDomainDN, strConfigDN);
  275. return(hr);
  276. }
  277. HRESULT
  278. myDomainFromDn(
  279. IN WCHAR const *pwszDN,
  280. OPTIONAL OUT WCHAR **ppwszDomainDNS)
  281. {
  282. HRESULT hr;
  283. DWORD cwcOut;
  284. WCHAR *pwszOut;
  285. WCHAR **ppwszExplodedDN = NULL;
  286. WCHAR **ppwsz;
  287. WCHAR wszDC[4];
  288. WCHAR *pwsz;
  289. *ppwszDomainDNS = NULL;
  290. ppwszExplodedDN = ldap_explode_dn(const_cast<WCHAR *>(pwszDN), 0);
  291. if (NULL == ppwszExplodedDN)
  292. {
  293. hr = myHLdapLastError(NULL, NULL);
  294. _JumpError(hr, error, "ldap_explode_dn");
  295. }
  296. cwcOut = 0;
  297. for (ppwsz = ppwszExplodedDN; NULL != *ppwsz; ppwsz++)
  298. {
  299. pwsz = *ppwsz;
  300. wcsncpy(wszDC, pwsz, ARRAYSIZE(wszDC) - 1);
  301. wszDC[ARRAYSIZE(wszDC) - 1] = L'\0';
  302. if (0 == lstrcmpi(wszDC, L"DC="))
  303. {
  304. pwsz += ARRAYSIZE(wszDC) - 1;
  305. if (0 != cwcOut)
  306. {
  307. cwcOut++;
  308. }
  309. cwcOut += wcslen(pwsz);
  310. }
  311. }
  312. pwszOut = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwcOut + 1) * sizeof(WCHAR));
  313. if (NULL == pwszOut)
  314. {
  315. hr = E_OUTOFMEMORY;
  316. _JumpError(hr, error, "LocalAlloc");
  317. }
  318. *ppwszDomainDNS = pwszOut;
  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 == lstrcmpi(wszDC, L"DC="))
  325. {
  326. pwsz += ARRAYSIZE(wszDC) - 1;
  327. if (pwszOut != *ppwszDomainDNS)
  328. {
  329. *pwszOut++ = L'.';
  330. }
  331. wcscpy(pwszOut, pwsz);
  332. pwszOut += wcslen(pwsz);
  333. }
  334. }
  335. CSASSERT(wcslen(*ppwszDomainDNS) == cwcOut);
  336. hr = S_OK;
  337. error:
  338. if (NULL != ppwszExplodedDN)
  339. {
  340. ldap_value_free(ppwszExplodedDN);
  341. }
  342. return(hr);
  343. }
  344. HRESULT
  345. myLdapOpen(
  346. OUT LDAP **ppld,
  347. OPTIONAL OUT BSTR *pstrDomainDN,
  348. OPTIONAL OUT BSTR *pstrConfigDN)
  349. {
  350. HRESULT hr;
  351. LDAP *pld = NULL;
  352. *ppld = NULL;
  353. CSASSERT(NULL == pstrConfigDN || NULL == *pstrConfigDN);
  354. CSASSERT(NULL == pstrDomainDN || NULL == *pstrDomainDN);
  355. hr = myRobustLdapBind(&pld, FALSE);
  356. _JumpIfError(hr, error, "myRobustLdapBind");
  357. // domain and config containers (%5, %6)
  358. hr = myGetAuthoritativeDomainDn(pld, pstrDomainDN, pstrConfigDN);
  359. if (S_OK != hr)
  360. {
  361. hr = myHError(hr);
  362. _JumpError(hr, error, "myGetAuthoritativeDomainDn");
  363. }
  364. *ppld = pld;
  365. pld = NULL;
  366. error:
  367. if (NULL != pld)
  368. {
  369. ldap_unbind(pld);
  370. }
  371. return(hr);
  372. }
  373. VOID
  374. myLdapClose(
  375. OPTIONAL IN LDAP *pld,
  376. OPTIONAL IN BSTR strDomainDN,
  377. OPTIONAL IN BSTR strConfigDN)
  378. {
  379. if (NULL != strDomainDN)
  380. {
  381. SysFreeString(strDomainDN);
  382. }
  383. if (NULL != strConfigDN)
  384. {
  385. SysFreeString(strConfigDN);
  386. }
  387. if (NULL != pld)
  388. {
  389. ldap_unbind(pld);
  390. }
  391. }
  392. BOOL
  393. myLdapRebindRequired(
  394. IN ULONG ldaperrParm,
  395. OPTIONAL IN LDAP *pld)
  396. {
  397. BOOL fRebindRequired = FALSE;
  398. if (LDAP_SERVER_DOWN == ldaperrParm ||
  399. LDAP_UNAVAILABLE == ldaperrParm ||
  400. LDAP_TIMEOUT == ldaperrParm ||
  401. NULL == pld)
  402. {
  403. fRebindRequired = TRUE;
  404. }
  405. else
  406. {
  407. ULONG ldaperr;
  408. VOID *pvReachable = NULL; // clear high bits for 64-bit machines
  409. ldaperr = ldap_get_option(pld, LDAP_OPT_HOST_REACHABLE, &pvReachable);
  410. if (LDAP_SUCCESS != ldaperr || LDAP_OPT_ON != pvReachable)
  411. {
  412. fRebindRequired = TRUE;
  413. }
  414. }
  415. return(fRebindRequired);
  416. }
  417. HRESULT
  418. myLdapGetDSHostName(
  419. IN LDAP *pld,
  420. OUT WCHAR **ppwszHostName)
  421. {
  422. HRESULT hr;
  423. ULONG ldaperr;
  424. ldaperr = ldap_get_option(pld, LDAP_OPT_HOST_NAME, ppwszHostName);
  425. if (LDAP_SUCCESS != ldaperr)
  426. {
  427. *ppwszHostName = NULL;
  428. }
  429. hr = myHLdapError(pld, ldaperr, NULL);
  430. return(hr);
  431. }
  432. HRESULT
  433. myLdapCreateContainer(
  434. IN LDAP *pld,
  435. IN WCHAR const *pwszDN,
  436. IN BOOL fSkipObject, // Does the DN contain a leaf object name
  437. IN DWORD cMaxLevel, // create this many nested containers as needed
  438. IN PSECURITY_DESCRIPTOR pContainerSD,
  439. OPTIONAL OUT WCHAR **ppwszError)
  440. {
  441. HRESULT hr;
  442. WCHAR const *pwsz = pwszDN;
  443. LDAPMod objectClass;
  444. LDAPMod advancedView;
  445. LDAPMod securityDescriptor;
  446. WCHAR *papwszshowInAdvancedViewOnly[2] = { L"TRUE", NULL };
  447. WCHAR *objectClassVals[3];
  448. LDAPMod *mods[4];
  449. struct berval *sdVals[2];
  450. struct berval sdberval;
  451. if (NULL != ppwszError)
  452. {
  453. *ppwszError = NULL;
  454. }
  455. mods[0] = &objectClass;
  456. mods[1] = &advancedView;
  457. mods[2] = &securityDescriptor;
  458. mods[3] = NULL;
  459. objectClass.mod_op = LDAP_MOD_ADD;
  460. objectClass.mod_type = TEXT("objectclass");
  461. objectClass.mod_values = objectClassVals;
  462. advancedView.mod_op = LDAP_MOD_ADD;
  463. advancedView.mod_type = TEXT("showInAdvancedViewOnly");
  464. advancedView.mod_values = papwszshowInAdvancedViewOnly;
  465. objectClassVals[0] = TEXT("top");
  466. objectClassVals[1] = TEXT("container");
  467. objectClassVals[2] = NULL;
  468. securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  469. securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
  470. securityDescriptor.mod_bvalues = sdVals;
  471. sdVals[0] = &sdberval;
  472. sdVals[1] = NULL;
  473. if (IsValidSecurityDescriptor(pContainerSD))
  474. {
  475. sdberval.bv_len = GetSecurityDescriptorLength(pContainerSD);
  476. sdberval.bv_val = (char *)pContainerSD;
  477. }
  478. else
  479. {
  480. sdberval.bv_len = 0;
  481. sdberval.bv_val = NULL;
  482. }
  483. // If the DN passed in was for the full object that goes in the container
  484. // (and not the container itself), skip past the CN for the leaf object.
  485. if (fSkipObject)
  486. {
  487. // Look for the CN of the container for this object.
  488. pwsz = wcsstr(&pwsz[3], L"CN=");
  489. if (NULL == pwsz)
  490. {
  491. // If there was no CN, then we are contained in an OU or DC,
  492. // and we don't need to do the create.
  493. hr = S_OK;
  494. goto error;
  495. }
  496. }
  497. if (0 != wcsncmp(pwsz, L"CN=", 3))
  498. {
  499. // We're not pointing to a simple container, so don't create this DN.
  500. hr = S_OK;
  501. goto error;
  502. }
  503. pwszDN = pwsz;
  504. if (0 != cMaxLevel)
  505. {
  506. pwsz = wcsstr(&pwsz[3], L"CN=");
  507. if (NULL != pwsz)
  508. {
  509. // The remaining DN is a container, so try to create it.
  510. hr = myLdapCreateContainer(
  511. pld,
  512. pwsz,
  513. FALSE,
  514. cMaxLevel - 1,
  515. pContainerSD,
  516. ppwszError);
  517. _JumpIfError(hr, error, "myLdapCreateContainer");
  518. CSASSERT(NULL == ppwszError || NULL == *ppwszError);
  519. }
  520. }
  521. DBGPRINT((DBG_SS_CERTLIBI, "Creating DS Container: '%ws'\n", pwszDN));
  522. // Create the container
  523. hr = ldap_add_ext_s(
  524. pld,
  525. const_cast<WCHAR *>(pwszDN),
  526. mods,
  527. g_rgLdapControls,
  528. NULL);
  529. _PrintIfErrorStr2(
  530. hr,
  531. "ldap_add_ext_s(container)",
  532. pwszDN,
  533. LDAP_ALREADY_EXISTS);
  534. if ((HRESULT) LDAP_SUCCESS != hr && (HRESULT) LDAP_ALREADY_EXISTS != hr)
  535. {
  536. hr = myHLdapError(pld, hr, ppwszError);
  537. _JumpIfErrorStr(hr, error, "ldap_add_ext_s(container)", pwszDN);
  538. }
  539. hr = S_OK;
  540. error:
  541. return(hr);
  542. }
  543. HRESULT
  544. TrimURLDN(
  545. IN WCHAR const *pwszIn,
  546. OPTIONAL OUT WCHAR **ppwszSuffix,
  547. OUT WCHAR **ppwszDN)
  548. {
  549. HRESULT hr;
  550. WCHAR *pwsz;
  551. if (NULL != ppwszSuffix)
  552. {
  553. *ppwszSuffix = NULL;
  554. }
  555. *ppwszDN = NULL;
  556. pwsz = wcschr(pwszIn, L':');
  557. if (NULL != pwsz)
  558. {
  559. pwszIn = &pwsz[1];
  560. }
  561. while (L'/' == *pwszIn)
  562. {
  563. pwszIn++;
  564. }
  565. hr = myDupString(pwszIn, ppwszDN);
  566. _JumpIfError(hr, error, "myDupString");
  567. pwsz = wcschr(*ppwszDN, L'?');
  568. if (NULL != pwsz)
  569. {
  570. *pwsz++ = L'\0';
  571. if (NULL != ppwszSuffix)
  572. {
  573. *ppwszSuffix = pwsz;
  574. }
  575. }
  576. CSASSERT(S_OK == hr);
  577. error:
  578. if (S_OK != hr && NULL != *ppwszDN)
  579. {
  580. LocalFree(*ppwszDN);
  581. *ppwszDN = NULL;
  582. }
  583. return(hr);
  584. }
  585. HRESULT
  586. CreateCertObject(
  587. IN LDAP *pld,
  588. IN WCHAR const *pwszDN,
  589. IN DWORD dwObjectType, // LPC_*
  590. OUT DWORD *pdwDisposition,
  591. OPTIONAL OUT WCHAR **ppwszError)
  592. {
  593. HRESULT hr;
  594. PSECURITY_DESCRIPTOR pSD = NULL;
  595. PSECURITY_DESCRIPTOR pContainerSD = NULL;
  596. *pdwDisposition = LDAP_OTHER;
  597. if (NULL != ppwszError)
  598. {
  599. *ppwszError = NULL;
  600. }
  601. // get default DS CA security descriptor
  602. hr = myGetSDFromTemplate(WSZ_DEFAULT_CA_DS_SECURITY, NULL, &pSD);
  603. _JumpIfError(hr, error, "myGetSDFromTemplate");
  604. // get default DS AIA security descriptor
  605. hr = myGetSDFromTemplate(WSZ_DEFAULT_CA_DS_SECURITY, NULL, &pContainerSD);
  606. _JumpIfError(hr, error, "myGetSDFromTemplate");
  607. if (LPC_CREATECONTAINER & dwObjectType)
  608. {
  609. hr = myLdapCreateContainer(
  610. pld,
  611. pwszDN,
  612. TRUE,
  613. 0,
  614. pContainerSD,
  615. ppwszError);
  616. if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) != hr)
  617. {
  618. _JumpIfError(hr, error, "myLdapCreateContainer");
  619. }
  620. }
  621. if (LPC_CREATEOBJECT & dwObjectType)
  622. {
  623. switch (LPC_OBJECTMASK & dwObjectType)
  624. {
  625. case LPC_CAOBJECT:
  626. hr = myLdapCreateCAObject(
  627. pld,
  628. pwszDN,
  629. NULL,
  630. 0,
  631. pSD,
  632. pdwDisposition,
  633. ppwszError);
  634. if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) != hr)
  635. {
  636. _JumpIfErrorStr(hr, error, "myLdapCreateCAObject", pwszDN);
  637. }
  638. break;
  639. case LPC_KRAOBJECT:
  640. case LPC_USEROBJECT:
  641. case LPC_MACHINEOBJECT:
  642. hr = myLdapCreateUserObject(
  643. pld,
  644. pwszDN,
  645. NULL,
  646. 0,
  647. pSD,
  648. dwObjectType,
  649. pdwDisposition,
  650. ppwszError);
  651. if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) != hr)
  652. {
  653. _JumpIfErrorStr(hr, error, "myLdapCreateUserObject", pwszDN);
  654. }
  655. break;
  656. default:
  657. hr = E_INVALIDARG;
  658. _JumpError(hr, error, "dwObjectType");
  659. }
  660. }
  661. hr = S_OK;
  662. error:
  663. if (NULL != pSD)
  664. {
  665. LocalFree(pSD);
  666. }
  667. if (NULL != pContainerSD)
  668. {
  669. LocalFree(pContainerSD);
  670. }
  671. return(hr);
  672. }
  673. HRESULT
  674. AddCertToAttribute(
  675. IN LDAP *pld,
  676. IN CERT_CONTEXT const *pccPublish,
  677. IN WCHAR const *pwszDN,
  678. IN WCHAR const *pwszAttribute,
  679. OUT DWORD *pdwDisposition,
  680. OPTIONAL OUT WCHAR **ppwszError)
  681. {
  682. HRESULT hr;
  683. DWORD cres;
  684. DWORD cber;
  685. DWORD iber;
  686. DWORD i;
  687. LDAP_TIMEVAL timeval;
  688. LDAPMessage *pmsg = NULL;
  689. LDAPMessage *pres;
  690. WCHAR *apwszAttrs[2];
  691. struct berval **ppberval = NULL;
  692. struct berval **prgpberVals = NULL;
  693. FILETIME ft;
  694. BOOL fDeleteExpiredCert = FALSE;
  695. BOOL fNewCertMissing = TRUE;
  696. *pdwDisposition = LDAP_OTHER;
  697. if (NULL != ppwszError)
  698. {
  699. *ppwszError = NULL;
  700. }
  701. apwszAttrs[0] = const_cast<WCHAR *>(pwszAttribute);
  702. apwszAttrs[1] = NULL;
  703. timeval.tv_sec = csecLDAPTIMEOUT;
  704. timeval.tv_usec = 0;
  705. hr = ldap_search_st(
  706. pld, // ld
  707. const_cast<WCHAR *>(pwszDN), // base
  708. LDAP_SCOPE_BASE, // scope
  709. NULL, // filter
  710. apwszAttrs, // attrs
  711. FALSE, // attrsonly
  712. &timeval, // timeout
  713. &pmsg); // res
  714. if (S_OK != hr)
  715. {
  716. *pdwDisposition = hr;
  717. hr = myHLdapError(pld, hr, NULL);
  718. _JumpErrorStr(hr, error, "ldap_search_st", pwszDN);
  719. }
  720. cres = ldap_count_entries(pld, pmsg);
  721. if (0 == cres)
  722. {
  723. // No entries were found.
  724. hr = NTE_NOT_FOUND;
  725. _JumpError(hr, error, "ldap_count_entries");
  726. }
  727. pres = ldap_first_entry(pld, pmsg);
  728. if (NULL == pres)
  729. {
  730. hr = NTE_NOT_FOUND;
  731. _JumpError(hr, error, "ldap_first_entry");
  732. }
  733. ppberval = ldap_get_values_len(
  734. pld,
  735. pres,
  736. const_cast<WCHAR *>(pwszAttribute));
  737. cber = 0;
  738. if (NULL != ppberval)
  739. {
  740. while (NULL != ppberval[cber])
  741. {
  742. cber++;
  743. }
  744. }
  745. prgpberVals = (struct berval **) LocalAlloc(
  746. LMEM_FIXED,
  747. (cber + 2) * sizeof(prgpberVals[0]));
  748. if (NULL == prgpberVals)
  749. {
  750. hr = E_OUTOFMEMORY;
  751. _JumpError(hr, error, "LocalAlloc");
  752. }
  753. // Delete any certs that are at least one day old
  754. GetSystemTimeAsFileTime(&ft);
  755. myMakeExprDateTime(&ft, -1, ENUM_PERIOD_DAYS);
  756. iber = 0;
  757. if (NULL != ppberval)
  758. {
  759. for (i = 0; NULL != ppberval[i]; i++)
  760. {
  761. BOOL fCopyBER = TRUE;
  762. struct berval *pberval = ppberval[i];
  763. if (pberval->bv_len == 1 && pberval->bv_val[0] == 0)
  764. {
  765. fCopyBER = FALSE; // remove zero byte placeholder value
  766. }
  767. else
  768. if (pccPublish->cbCertEncoded == pberval->bv_len &&
  769. 0 == memcmp(
  770. pberval->bv_val,
  771. pccPublish->pbCertEncoded,
  772. pccPublish->cbCertEncoded))
  773. {
  774. fCopyBER = FALSE; // remove duplicates to avoid ldap error
  775. fNewCertMissing = FALSE;
  776. }
  777. else
  778. {
  779. CERT_CONTEXT const *pcc;
  780. pcc = CertCreateCertificateContext(
  781. X509_ASN_ENCODING,
  782. (BYTE *) pberval->bv_val,
  783. pberval->bv_len);
  784. if (NULL == pcc)
  785. {
  786. hr = myHLastError();
  787. _PrintError(hr, "CertCreateCertificateContext");
  788. }
  789. else
  790. {
  791. if (0 > CompareFileTime(&pcc->pCertInfo->NotAfter, &ft))
  792. {
  793. fCopyBER = FALSE;
  794. fDeleteExpiredCert = TRUE;
  795. DBGPRINT((DBG_SS_CERTLIB, "Deleting expired cert %u\n", i));
  796. }
  797. CertFreeCertificateContext(pcc);
  798. }
  799. }
  800. if (fCopyBER)
  801. {
  802. prgpberVals[iber++] = pberval;
  803. }
  804. }
  805. }
  806. // set disposition assuming there's nothing to do:
  807. *pdwDisposition = LDAP_ATTRIBUTE_OR_VALUE_EXISTS;
  808. if (fNewCertMissing || fDeleteExpiredCert)
  809. {
  810. struct berval certberval;
  811. LDAPMod *mods[2];
  812. LDAPMod certmod;
  813. mods[0] = &certmod;
  814. mods[1] = NULL;
  815. certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  816. certmod.mod_type = const_cast<WCHAR *>(pwszAttribute);
  817. certmod.mod_bvalues = prgpberVals;
  818. prgpberVals[iber++] = &certberval;
  819. prgpberVals[iber] = NULL;
  820. certberval.bv_val = (char *) pccPublish->pbCertEncoded;
  821. certberval.bv_len = pccPublish->cbCertEncoded;
  822. hr = ldap_modify_ext_s(
  823. pld,
  824. const_cast<WCHAR *>(pwszDN),
  825. mods,
  826. NULL,
  827. NULL);
  828. *pdwDisposition = hr;
  829. if (hr != S_OK)
  830. {
  831. hr = myHLdapError(pld, hr, ppwszError);
  832. _JumpError(hr, error, "ldap_modify_ext_s");
  833. }
  834. }
  835. hr = S_OK;
  836. error:
  837. if (NULL != prgpberVals)
  838. {
  839. LocalFree(prgpberVals);
  840. }
  841. if (NULL != ppberval)
  842. {
  843. ldap_value_free_len(ppberval);
  844. }
  845. if (NULL != pmsg)
  846. {
  847. ldap_msgfree(pmsg);
  848. }
  849. return(hr);
  850. }
  851. HRESULT
  852. AddCRLToAttribute(
  853. IN LDAP *pld,
  854. IN CRL_CONTEXT const *pCRLPublish,
  855. IN WCHAR const *pwszDN,
  856. IN WCHAR const *pwszAttribute,
  857. OUT DWORD *pdwDisposition,
  858. OPTIONAL OUT WCHAR **ppwszError)
  859. {
  860. HRESULT hr;
  861. DWORD cres;
  862. LDAP_TIMEVAL timeval;
  863. LDAPMessage *pmsg = NULL;
  864. LDAPMessage *pres;
  865. WCHAR *apwszAttrs[2];
  866. struct berval **ppberval = NULL;
  867. LDAPMod crlmod;
  868. LDAPMod *mods[2];
  869. struct berval *crlberVals[2];
  870. struct berval crlberval;
  871. *pdwDisposition = LDAP_OTHER;
  872. if (NULL != ppwszError)
  873. {
  874. *ppwszError = NULL;
  875. }
  876. apwszAttrs[0] = const_cast<WCHAR *>(pwszAttribute);
  877. apwszAttrs[1] = NULL;
  878. timeval.tv_sec = csecLDAPTIMEOUT;
  879. timeval.tv_usec = 0;
  880. hr = ldap_search_st(
  881. pld, // ld
  882. const_cast<WCHAR *>(pwszDN), // base
  883. LDAP_SCOPE_BASE, // scope
  884. NULL, // filter
  885. apwszAttrs, // attrs
  886. FALSE, // attrsonly
  887. &timeval, // timeout
  888. &pmsg); // res
  889. if (S_OK != hr)
  890. {
  891. *pdwDisposition = hr;
  892. hr = myHLdapError(pld, hr, NULL);
  893. _JumpErrorStr(hr, error, "ldap_search_st", pwszDN);
  894. }
  895. cres = ldap_count_entries(pld, pmsg);
  896. if (0 == cres)
  897. {
  898. // No entries were found.
  899. hr = NTE_NOT_FOUND;
  900. _JumpError(hr, error, "ldap_count_entries");
  901. }
  902. pres = ldap_first_entry(pld, pmsg);
  903. if (NULL == pres)
  904. {
  905. hr = NTE_NOT_FOUND;
  906. _JumpError(hr, error, "ldap_first_entry");
  907. }
  908. ppberval = ldap_get_values_len(
  909. pld,
  910. pres,
  911. const_cast<WCHAR *>(pwszAttribute));
  912. if (NULL != ppberval &&
  913. NULL != ppberval[0] &&
  914. pCRLPublish->cbCrlEncoded == ppberval[0]->bv_len &&
  915. 0 == memcmp(
  916. ppberval[0]->bv_val,
  917. pCRLPublish->pbCrlEncoded,
  918. pCRLPublish->cbCrlEncoded))
  919. {
  920. // set disposition assuming there's nothing to do:
  921. *pdwDisposition = LDAP_ATTRIBUTE_OR_VALUE_EXISTS;
  922. }
  923. else
  924. {
  925. mods[0] = &crlmod;
  926. mods[1] = NULL;
  927. crlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  928. crlmod.mod_type = const_cast<WCHAR *>(pwszAttribute);
  929. crlmod.mod_bvalues = crlberVals;
  930. crlberVals[0] = &crlberval;
  931. crlberVals[1] = NULL;
  932. crlberval.bv_val = (char *) pCRLPublish->pbCrlEncoded;
  933. crlberval.bv_len = pCRLPublish->cbCrlEncoded;
  934. hr = ldap_modify_ext_s(
  935. pld,
  936. const_cast<WCHAR *>(pwszDN),
  937. mods,
  938. NULL,
  939. NULL);
  940. *pdwDisposition = hr;
  941. if (hr != S_OK)
  942. {
  943. hr = myHLdapError(pld, hr, ppwszError);
  944. _JumpError(hr, error, "ldap_modify_ext_s");
  945. }
  946. }
  947. hr = S_OK;
  948. error:
  949. if (NULL != ppberval)
  950. {
  951. ldap_value_free_len(ppberval);
  952. }
  953. if (NULL != pmsg)
  954. {
  955. ldap_msgfree(pmsg);
  956. }
  957. return(hr);
  958. }
  959. HRESULT
  960. myLdapPublishCertToDS(
  961. IN LDAP *pld,
  962. IN CERT_CONTEXT const *pccPublish,
  963. IN WCHAR const *pwszURL,
  964. IN WCHAR const *pwszAttribute,
  965. IN DWORD dwObjectType, // LPC_*
  966. OUT DWORD *pdwDisposition,
  967. OPTIONAL OUT WCHAR **ppwszError)
  968. {
  969. HRESULT hr;
  970. WCHAR *pwszDN = NULL;
  971. WCHAR *pwszSuffix;
  972. *pdwDisposition = LDAP_OTHER;
  973. if (NULL != ppwszError)
  974. {
  975. *ppwszError = NULL;
  976. }
  977. hr = TrimURLDN(pwszURL, &pwszSuffix, &pwszDN);
  978. _JumpIfError(hr, error, "TrimURLDN");
  979. if (0 == lstrcmpi(wszDSUSERCERTATTRIBUTE, pwszAttribute) ||
  980. 0 == lstrcmpi(wszDSKRACERTATTRIBUTE, pwszAttribute))
  981. {
  982. if (LPC_CAOBJECT == (LPC_OBJECTMASK & dwObjectType))
  983. {
  984. hr = E_INVALIDARG;
  985. }
  986. }
  987. else
  988. if (0 == lstrcmpi(wszDSCACERTATTRIBUTE, pwszAttribute) ||
  989. 0 == lstrcmpi(wszDSCROSSCERTPAIRATTRIBUTE, pwszAttribute))
  990. {
  991. if (LPC_CAOBJECT != (LPC_OBJECTMASK & dwObjectType))
  992. {
  993. hr = E_INVALIDARG;
  994. }
  995. }
  996. else
  997. {
  998. hr = E_INVALIDARG;
  999. }
  1000. _JumpIfErrorStr(hr, error, "Bad Cert Attribute", pwszAttribute);
  1001. *pdwDisposition = LDAP_SUCCESS;
  1002. if ((LPC_CREATECONTAINER | LPC_CREATEOBJECT) & dwObjectType)
  1003. {
  1004. hr = CreateCertObject(
  1005. pld,
  1006. pwszDN,
  1007. dwObjectType,
  1008. pdwDisposition,
  1009. ppwszError);
  1010. _JumpIfError(hr, error, "CreateCertObject");
  1011. CSASSERT(NULL == ppwszError || NULL == *ppwszError);
  1012. }
  1013. hr = AddCertToAttribute(
  1014. pld,
  1015. pccPublish,
  1016. pwszDN,
  1017. pwszAttribute,
  1018. pdwDisposition,
  1019. ppwszError);
  1020. _JumpIfError(hr, error, "AddCertToAttribute");
  1021. CSASSERT(NULL == ppwszError || NULL == *ppwszError);
  1022. error:
  1023. if (NULL != pwszDN)
  1024. {
  1025. LocalFree(pwszDN);
  1026. }
  1027. return(hr);
  1028. }
  1029. HRESULT
  1030. myLdapPublishCRLToDS(
  1031. IN LDAP *pld,
  1032. IN CRL_CONTEXT const *pCRLPublish,
  1033. IN WCHAR const *pwszURL,
  1034. IN WCHAR const *pwszAttribute,
  1035. OUT DWORD *pdwDisposition,
  1036. OPTIONAL OUT WCHAR **ppwszError)
  1037. {
  1038. HRESULT hr;
  1039. WCHAR *pwszDN = NULL;
  1040. WCHAR *pwszSuffix;
  1041. PSECURITY_DESCRIPTOR pSD = NULL;
  1042. PSECURITY_DESCRIPTOR pContainerSD = NULL;
  1043. *pdwDisposition = LDAP_OTHER;
  1044. if (NULL != ppwszError)
  1045. {
  1046. *ppwszError = NULL;
  1047. }
  1048. hr = TrimURLDN(pwszURL, &pwszSuffix, &pwszDN);
  1049. _JumpIfError(hr, error, "TrimURLDN");
  1050. if (0 == lstrcmpi(wszDSBASECRLATTRIBUTE, pwszAttribute))
  1051. {
  1052. }
  1053. else if (0 == lstrcmpi(wszDSDELTACRLATTRIBUTE, pwszAttribute))
  1054. {
  1055. }
  1056. else
  1057. {
  1058. hr = E_INVALIDARG;
  1059. _JumpErrorStr(hr, error, "Bad CRL Attribute", pwszAttribute);
  1060. }
  1061. // get default DS CDP security descriptor
  1062. hr = myGetSDFromTemplate(WSZ_DEFAULT_CDP_DS_SECURITY, NULL, &pSD);
  1063. if (S_OK != hr)
  1064. {
  1065. _PrintError(hr, "myGetSDFromTemplate");
  1066. pSD = NULL;
  1067. }
  1068. // get default DS AIA security descriptor
  1069. hr = myGetSDFromTemplate(WSZ_DEFAULT_CA_DS_SECURITY, NULL, &pContainerSD);
  1070. _JumpIfError(hr, error, "myGetSDFromTemplate");
  1071. hr = myLdapCreateContainer(pld, pwszDN, TRUE, 1, pContainerSD, ppwszError);
  1072. _JumpIfError(hr, error, "myLdapCreateContainer");
  1073. hr = myLdapCreateCDPObject(
  1074. pld,
  1075. pwszDN,
  1076. NULL != pSD? pSD : pContainerSD,
  1077. pdwDisposition,
  1078. ppwszError);
  1079. _JumpIfErrorStr(hr, error, "myLdapCreateCDPObject", pwszDN);
  1080. hr = AddCRLToAttribute(
  1081. pld,
  1082. pCRLPublish,
  1083. pwszDN,
  1084. pwszAttribute,
  1085. pdwDisposition,
  1086. ppwszError);
  1087. _JumpIfError(hr, error, "AddCRLToAttribute");
  1088. error:
  1089. if (NULL != pSD)
  1090. {
  1091. LocalFree(pSD);
  1092. }
  1093. if (NULL != pContainerSD)
  1094. {
  1095. LocalFree(pContainerSD);
  1096. }
  1097. if (NULL != pwszDN)
  1098. {
  1099. LocalFree(pwszDN);
  1100. }
  1101. return(hr);
  1102. }
  1103. HRESULT
  1104. CreateOrUpdateDSObject(
  1105. IN LDAP *pld,
  1106. IN WCHAR const *pwszDN,
  1107. IN LDAPMod **prgmodsCreate,
  1108. OPTIONAL IN LDAPMod **prgmodsUpdate,
  1109. OUT DWORD *pdwDisposition,
  1110. OPTIONAL OUT WCHAR **ppwszError)
  1111. {
  1112. HRESULT hr;
  1113. ULONG ldaperr;
  1114. if (NULL != ppwszError)
  1115. {
  1116. *ppwszError = NULL;
  1117. }
  1118. ldaperr = ldap_add_ext_s(
  1119. pld,
  1120. const_cast<WCHAR *>(pwszDN),
  1121. prgmodsCreate,
  1122. g_rgLdapControls,
  1123. NULL);
  1124. *pdwDisposition = ldaperr;
  1125. _PrintIfErrorStr2(ldaperr, "ldap_add_ext_s", pwszDN, LDAP_ALREADY_EXISTS);
  1126. if (ldaperr == LDAP_ALREADY_EXISTS)
  1127. {
  1128. if (NULL == prgmodsUpdate)
  1129. {
  1130. ldaperr = LDAP_SUCCESS;
  1131. }
  1132. else
  1133. {
  1134. ldaperr = ldap_modify_ext_s(
  1135. pld,
  1136. const_cast<WCHAR *>(pwszDN),
  1137. prgmodsUpdate,
  1138. NULL,
  1139. NULL);
  1140. *pdwDisposition = ldaperr;
  1141. _PrintIfErrorStr2(
  1142. ldaperr,
  1143. "ldap_modify_ext_s",
  1144. pwszDN,
  1145. LDAP_ATTRIBUTE_OR_VALUE_EXISTS);
  1146. if (LDAP_ATTRIBUTE_OR_VALUE_EXISTS == ldaperr)
  1147. {
  1148. ldaperr = LDAP_SUCCESS;
  1149. }
  1150. }
  1151. }
  1152. if (ldaperr != LDAP_SUCCESS)
  1153. {
  1154. hr = myHLdapError(pld, ldaperr, ppwszError);
  1155. _JumpError(hr, error, "Add/Update DS");
  1156. }
  1157. hr = S_OK;
  1158. error:
  1159. return(hr);
  1160. }
  1161. HRESULT
  1162. myLdapCreateCAObject(
  1163. IN LDAP *pld,
  1164. IN WCHAR const *pwszDN,
  1165. OPTIONAL IN BYTE const *pbCert,
  1166. IN DWORD cbCert,
  1167. IN PSECURITY_DESCRIPTOR pSD,
  1168. OUT DWORD *pdwDisposition,
  1169. OPTIONAL OUT WCHAR **ppwszError)
  1170. {
  1171. HRESULT hr;
  1172. BYTE ZeroByte = 0;
  1173. LDAPMod objectClass;
  1174. LDAPMod securityDescriptor;
  1175. LDAPMod crlmod;
  1176. LDAPMod arlmod;
  1177. LDAPMod certmod;
  1178. struct berval sdberval;
  1179. struct berval crlberval;
  1180. struct berval arlberval;
  1181. struct berval certberval;
  1182. WCHAR *objectClassVals[3];
  1183. struct berval *sdVals[2];
  1184. struct berval *crlVals[2];
  1185. struct berval *arlVals[2];
  1186. struct berval *certVals[2];
  1187. LDAPMod *mods[6];
  1188. if (NULL != ppwszError)
  1189. {
  1190. *ppwszError = NULL;
  1191. }
  1192. mods[0] = &objectClass;
  1193. mods[1] = &securityDescriptor;
  1194. mods[2] = &crlmod;
  1195. mods[3] = &arlmod;
  1196. mods[4] = &certmod; // must be last!
  1197. mods[5] = NULL;
  1198. objectClass.mod_op = LDAP_MOD_ADD;
  1199. objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
  1200. objectClass.mod_values = objectClassVals;
  1201. objectClassVals[0] = wszDSTOPCLASSNAME;
  1202. objectClassVals[1] = wszDSCACLASSNAME;
  1203. objectClassVals[2] = NULL;
  1204. securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  1205. securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
  1206. securityDescriptor.mod_bvalues = sdVals;
  1207. sdVals[0] = &sdberval;
  1208. sdVals[1] = NULL;
  1209. sdberval.bv_len = 0;
  1210. sdberval.bv_val = NULL;
  1211. if (IsValidSecurityDescriptor(pSD))
  1212. {
  1213. sdberval.bv_len = GetSecurityDescriptorLength(pSD);
  1214. sdberval.bv_val = (char *) pSD;
  1215. }
  1216. crlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  1217. crlmod.mod_type = wszDSBASECRLATTRIBUTE;
  1218. crlmod.mod_bvalues = crlVals;
  1219. crlVals[0] = &crlberval;
  1220. crlVals[1] = NULL;
  1221. crlberval.bv_len = sizeof(ZeroByte);
  1222. crlberval.bv_val = (char *) &ZeroByte;
  1223. arlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  1224. arlmod.mod_type = wszDSAUTHORITYCRLATTRIBUTE;
  1225. arlmod.mod_bvalues = arlVals;
  1226. arlVals[0] = &arlberval;
  1227. arlVals[1] = NULL;
  1228. arlberval.bv_len = sizeof(ZeroByte);
  1229. arlberval.bv_val = (char *) &ZeroByte;
  1230. certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  1231. certmod.mod_type = wszDSCACERTATTRIBUTE;
  1232. certmod.mod_bvalues = certVals;
  1233. certVals[0] = &certberval;
  1234. certVals[1] = NULL;
  1235. certberval.bv_len = sizeof(ZeroByte);
  1236. certberval.bv_val = (char *) &ZeroByte;
  1237. if (NULL != pbCert)
  1238. {
  1239. certberval.bv_len = cbCert;
  1240. certberval.bv_val = (char *) pbCert;
  1241. }
  1242. DBGPRINT((DBG_SS_CERTLIBI, "Creating DS CA Object: '%ws'\n", pwszDN));
  1243. CSASSERT(&certmod == mods[ARRAYSIZE(mods) - 2]);
  1244. hr = CreateOrUpdateDSObject(
  1245. pld,
  1246. pwszDN,
  1247. mods,
  1248. NULL != pbCert? &mods[ARRAYSIZE(mods) - 2] : NULL,
  1249. pdwDisposition,
  1250. ppwszError);
  1251. _JumpIfError(hr, error, "CreateOrUpdateDSObject(CA object)");
  1252. error:
  1253. return(hr);
  1254. }
  1255. HRESULT
  1256. myLdapCreateUserObject(
  1257. IN LDAP *pld,
  1258. IN WCHAR const *pwszDN,
  1259. OPTIONAL IN BYTE const *pbCert,
  1260. IN DWORD cbCert,
  1261. IN PSECURITY_DESCRIPTOR pSD,
  1262. IN DWORD dwObjectType, // LPC_* (but LPC_CREATE* is ignored)
  1263. OUT DWORD *pdwDisposition,
  1264. OPTIONAL OUT WCHAR **ppwszError)
  1265. {
  1266. HRESULT hr;
  1267. ULONG ldaperr;
  1268. BYTE ZeroByte = 0;
  1269. LDAPMod objectClass;
  1270. LDAPMod securityDescriptor;
  1271. LDAPMod certmod;
  1272. struct berval sdberval;
  1273. struct berval certberval;
  1274. WCHAR *objectClassVals[6];
  1275. struct berval *sdVals[2];
  1276. struct berval *certVals[2];
  1277. LDAPMod *mods[4];
  1278. if (NULL != ppwszError)
  1279. {
  1280. *ppwszError = NULL;
  1281. }
  1282. mods[0] = &objectClass;
  1283. mods[1] = &securityDescriptor;
  1284. mods[2] = &certmod; // must be last!
  1285. mods[3] = NULL;
  1286. securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  1287. securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
  1288. securityDescriptor.mod_bvalues = sdVals;
  1289. sdVals[0] = &sdberval;
  1290. sdVals[1] = NULL;
  1291. sdberval.bv_len = 0;
  1292. sdberval.bv_val = NULL;
  1293. if (IsValidSecurityDescriptor(pSD))
  1294. {
  1295. sdberval.bv_len = GetSecurityDescriptorLength(pSD);
  1296. sdberval.bv_val = (char *) pSD;
  1297. }
  1298. objectClass.mod_op = LDAP_MOD_ADD;
  1299. objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
  1300. objectClass.mod_values = objectClassVals;
  1301. DBGCODE(WCHAR const *pwszObjectType);
  1302. switch (LPC_OBJECTMASK & dwObjectType)
  1303. {
  1304. case LPC_KRAOBJECT:
  1305. objectClassVals[0] = wszDSTOPCLASSNAME;
  1306. objectClassVals[1] = wszDSKRACLASSNAME;
  1307. objectClassVals[2] = NULL;
  1308. DBGCODE(pwszObjectType = L"KRA");
  1309. break;
  1310. case LPC_USEROBJECT:
  1311. objectClassVals[0] = wszDSTOPCLASSNAME;
  1312. objectClassVals[1] = wszDSPERSONCLASSNAME;
  1313. objectClassVals[2] = wszDSORGPERSONCLASSNAME;
  1314. objectClassVals[3] = wszDSUSERCLASSNAME;
  1315. objectClassVals[4] = NULL;
  1316. DBGCODE(pwszObjectType = L"User");
  1317. break;
  1318. case LPC_MACHINEOBJECT:
  1319. objectClassVals[0] = wszDSTOPCLASSNAME;
  1320. objectClassVals[1] = wszDSPERSONCLASSNAME;
  1321. objectClassVals[2] = wszDSORGPERSONCLASSNAME;
  1322. objectClassVals[3] = wszDSUSERCLASSNAME;
  1323. objectClassVals[4] = wszDSMACHINECLASSNAME;
  1324. objectClassVals[5] = NULL;
  1325. DBGCODE(pwszObjectType = L"Machine");
  1326. break;
  1327. default:
  1328. hr = E_INVALIDARG;
  1329. _JumpError(hr, error, "dwObjectType");
  1330. }
  1331. certmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  1332. certmod.mod_type = wszDSUSERCERTATTRIBUTE;
  1333. certmod.mod_bvalues = certVals;
  1334. certVals[0] = &certberval;
  1335. certVals[1] = NULL;
  1336. certberval.bv_len = sizeof(ZeroByte);
  1337. certberval.bv_val = (char *) &ZeroByte;
  1338. if (NULL != pbCert)
  1339. {
  1340. certberval.bv_len = cbCert;
  1341. certberval.bv_val = (char *) pbCert;
  1342. }
  1343. DBGPRINT((
  1344. DBG_SS_CERTLIBI,
  1345. "Creating DS %ws Object: '%ws'\n",
  1346. pwszObjectType,
  1347. pwszDN));
  1348. CSASSERT(&certmod == mods[ARRAYSIZE(mods) - 2]);
  1349. hr = CreateOrUpdateDSObject(
  1350. pld,
  1351. pwszDN,
  1352. mods,
  1353. NULL != pbCert? &mods[ARRAYSIZE(mods) - 2] : NULL,
  1354. pdwDisposition,
  1355. ppwszError);
  1356. _JumpIfError(hr, error, "CreateOrUpdateDSObject(KRA object)");
  1357. error:
  1358. return(hr);
  1359. }
  1360. HRESULT
  1361. myLdapCreateCDPObject(
  1362. IN LDAP *pld,
  1363. IN WCHAR const *pwszDN,
  1364. IN PSECURITY_DESCRIPTOR pSD,
  1365. OUT DWORD *pdwDisposition,
  1366. OPTIONAL OUT WCHAR **ppwszError)
  1367. {
  1368. HRESULT hr;
  1369. BYTE ZeroByte = 0;
  1370. LDAPMod objectClass;
  1371. LDAPMod securityDescriptor;
  1372. LDAPMod crlmod;
  1373. LDAPMod drlmod;
  1374. struct berval sdberval;
  1375. struct berval crlberval;
  1376. struct berval drlberval;
  1377. WCHAR *objectClassVals[3];
  1378. struct berval *sdVals[2];
  1379. struct berval *crlVals[2];
  1380. struct berval *drlVals[2];
  1381. LDAPMod *mods[5];
  1382. if (NULL != ppwszError)
  1383. {
  1384. *ppwszError = NULL;
  1385. }
  1386. mods[0] = &objectClass;
  1387. mods[1] = &securityDescriptor;
  1388. mods[2] = &crlmod;
  1389. mods[3] = &drlmod;
  1390. mods[4] = NULL;
  1391. objectClass.mod_op = LDAP_MOD_ADD;
  1392. objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
  1393. objectClass.mod_values = objectClassVals;
  1394. objectClassVals[0] = wszDSTOPCLASSNAME;
  1395. objectClassVals[1] = wszDSCDPCLASSNAME;
  1396. objectClassVals[2] = NULL;
  1397. securityDescriptor.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_ADD;
  1398. securityDescriptor.mod_type = CERTTYPE_SECURITY_DESCRIPTOR_NAME;
  1399. securityDescriptor.mod_bvalues = sdVals;
  1400. sdVals[0] = &sdberval;
  1401. sdVals[1] = NULL;
  1402. sdberval.bv_len = 0;
  1403. sdberval.bv_val = NULL;
  1404. if (IsValidSecurityDescriptor(pSD))
  1405. {
  1406. sdberval.bv_len = GetSecurityDescriptorLength(pSD);
  1407. sdberval.bv_val = (char *) pSD;
  1408. }
  1409. crlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  1410. crlmod.mod_type = wszDSBASECRLATTRIBUTE;
  1411. crlmod.mod_bvalues = crlVals;
  1412. crlVals[0] = &crlberval;
  1413. crlVals[1] = NULL;
  1414. crlberval.bv_val = (char *) &ZeroByte;
  1415. crlberval.bv_len = sizeof(ZeroByte);
  1416. drlmod.mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
  1417. drlmod.mod_type = wszDSDELTACRLATTRIBUTE;
  1418. drlmod.mod_bvalues = drlVals;
  1419. drlVals[0] = &drlberval;
  1420. drlVals[1] = NULL;
  1421. drlberval.bv_val = (char *) &ZeroByte;
  1422. drlberval.bv_len = sizeof(ZeroByte);
  1423. DBGPRINT((DBG_SS_CERTLIBI, "Creating DS CDP Object: '%ws'\n", pwszDN));
  1424. hr = CreateOrUpdateDSObject(
  1425. pld,
  1426. pwszDN,
  1427. mods,
  1428. NULL,
  1429. pdwDisposition,
  1430. ppwszError);
  1431. _JumpIfError(hr, error, "CreateOrUpdateDSObject(CDP object)");
  1432. error:
  1433. return(hr);
  1434. }
  1435. HRESULT
  1436. myLdapCreateOIDObject(
  1437. IN LDAP *pld,
  1438. IN WCHAR const *pwszDN,
  1439. IN DWORD dwType,
  1440. IN WCHAR const *pwszObjId,
  1441. OUT DWORD *pdwDisposition,
  1442. OPTIONAL OUT WCHAR **ppwszError)
  1443. {
  1444. HRESULT hr;
  1445. WCHAR awcType[22];
  1446. LDAPMod objectClass;
  1447. LDAPMod typemod;
  1448. LDAPMod oidmod;
  1449. WCHAR *objectClassVals[3];
  1450. WCHAR *typeVals[2];
  1451. WCHAR *oidVals[2];
  1452. LDAPMod *mods[4];
  1453. if (NULL != ppwszError)
  1454. {
  1455. *ppwszError = NULL;
  1456. }
  1457. mods[0] = &objectClass;
  1458. mods[1] = &typemod;
  1459. mods[2] = &oidmod;
  1460. mods[3] = NULL;
  1461. CSASSERT(4 == ARRAYSIZE(mods));
  1462. objectClass.mod_op = LDAP_MOD_ADD;
  1463. objectClass.mod_type = wszDSOBJECTCLASSATTRIBUTE;
  1464. objectClass.mod_values = objectClassVals;
  1465. objectClassVals[0] = wszDSTOPCLASSNAME;
  1466. objectClassVals[1] = wszDSOIDCLASSNAME;
  1467. objectClassVals[2] = NULL;
  1468. CSASSERT(3 == ARRAYSIZE(objectClassVals));
  1469. typemod.mod_op = LDAP_MOD_ADD;
  1470. typemod.mod_type = OID_PROP_TYPE;
  1471. typemod.mod_values = typeVals;
  1472. wsprintf(awcType, L"%u", dwType);
  1473. typeVals[0] = awcType;
  1474. typeVals[1] = NULL;
  1475. CSASSERT(2 == ARRAYSIZE(typeVals));
  1476. oidmod.mod_op = LDAP_MOD_ADD;
  1477. oidmod.mod_type = OID_PROP_OID;
  1478. oidmod.mod_values = oidVals;
  1479. oidVals[0] = const_cast<WCHAR *>(pwszObjId);
  1480. oidVals[1] = NULL;
  1481. CSASSERT(2 == ARRAYSIZE(oidVals));
  1482. DBGPRINT((DBG_SS_CERTLIBI, "Creating DS OID Object: '%ws'\n", pwszDN));
  1483. hr = CreateOrUpdateDSObject(
  1484. pld,
  1485. pwszDN,
  1486. mods,
  1487. NULL,
  1488. pdwDisposition,
  1489. ppwszError);
  1490. _JumpIfError(hr, error, "CreateOrUpdateDSObject(OID object)");
  1491. error:
  1492. return(hr);
  1493. }
  1494. HRESULT
  1495. myLdapOIDIsMatchingLangId(
  1496. IN WCHAR const *pwszDisplayName,
  1497. IN DWORD dwLanguageId,
  1498. OUT BOOL *pfLangIdExists)
  1499. {
  1500. DWORD DisplayLangId = _wtoi(pwszDisplayName);
  1501. *pfLangIdExists = FALSE;
  1502. if (iswdigit(*pwszDisplayName) &&
  1503. NULL != wcschr(pwszDisplayName, L',') &&
  1504. DisplayLangId == dwLanguageId)
  1505. {
  1506. *pfLangIdExists = TRUE;
  1507. }
  1508. return(S_OK);
  1509. }
  1510. HRESULT
  1511. myLdapAddOIDDisplayNameToAttribute(
  1512. IN LDAP *pld,
  1513. OPTIONAL IN WCHAR **ppwszOld,
  1514. IN DWORD dwLanguageId,
  1515. IN WCHAR const *pwszDisplayName,
  1516. IN WCHAR const *pwszDN,
  1517. IN WCHAR const *pwszAttribute,
  1518. OUT DWORD *pdwDisposition,
  1519. OPTIONAL OUT WCHAR **ppwszError)
  1520. {
  1521. HRESULT hr;
  1522. DWORD cname;
  1523. DWORD iname;
  1524. DWORD i;
  1525. WCHAR **ppwszNew = NULL;
  1526. WCHAR *pwszNew = NULL;
  1527. BOOL fDeleteOldName = FALSE;
  1528. BOOL fNewNameMissing = TRUE;
  1529. *pdwDisposition = LDAP_OTHER;
  1530. if (NULL != ppwszError)
  1531. {
  1532. *ppwszError = NULL;
  1533. }
  1534. pwszNew = (WCHAR *) LocalAlloc(
  1535. LMEM_FIXED,
  1536. (12 + 1 + wcslen(pwszDisplayName) + 1) * sizeof(WCHAR));
  1537. if (NULL == pwszNew)
  1538. {
  1539. hr = E_OUTOFMEMORY;
  1540. _JumpError(hr, error, "LocalAlloc");
  1541. }
  1542. wsprintf(pwszNew, L"%u,%ws", dwLanguageId, pwszDisplayName);
  1543. cname = 0;
  1544. if (NULL != ppwszOld)
  1545. {
  1546. while (NULL != ppwszOld[cname])
  1547. {
  1548. cname++;
  1549. }
  1550. }
  1551. ppwszNew = (WCHAR **) LocalAlloc(
  1552. LMEM_FIXED,
  1553. (cname + 2) * sizeof(ppwszNew[0]));
  1554. if (NULL == ppwszNew)
  1555. {
  1556. hr = E_OUTOFMEMORY;
  1557. _JumpError(hr, error, "LocalAlloc");
  1558. }
  1559. // Delete any display names with matching dwLanguageId
  1560. iname = 0;
  1561. if (NULL != ppwszOld)
  1562. {
  1563. for (i = 0; NULL != ppwszOld[i]; i++)
  1564. {
  1565. BOOL fCopy = TRUE;
  1566. WCHAR *pwsz = ppwszOld[i];
  1567. // case-sensitive compare:
  1568. if (0 == lstrcmp(pwszNew, ppwszOld[i]))
  1569. {
  1570. fCopy = FALSE; // remove duplicates to avoid ldap error
  1571. fNewNameMissing = FALSE;
  1572. }
  1573. else
  1574. {
  1575. BOOL fLangIdExists;
  1576. hr = myLdapOIDIsMatchingLangId(
  1577. pwsz,
  1578. dwLanguageId,
  1579. &fLangIdExists);
  1580. _PrintIfError(hr, "myLdapOIDIsMatchingLangId");
  1581. if (S_OK != hr || fLangIdExists)
  1582. {
  1583. fCopy = FALSE;
  1584. fDeleteOldName = TRUE;
  1585. DBGPRINT((DBG_SS_CERTLIB, "Deleting %ws\n", pwsz));
  1586. }
  1587. }
  1588. if (fCopy)
  1589. {
  1590. ppwszNew[iname++] = pwsz;
  1591. }
  1592. }
  1593. }
  1594. CSASSERT(iname <= cname);
  1595. // set disposition assuming there's nothing to do:
  1596. *pdwDisposition = LDAP_ATTRIBUTE_OR_VALUE_EXISTS;
  1597. if (fNewNameMissing || fDeleteOldName)
  1598. {
  1599. LDAPMod *mods[2];
  1600. LDAPMod namemod;
  1601. mods[0] = &namemod;
  1602. mods[1] = NULL;
  1603. namemod.mod_op = LDAP_MOD_REPLACE;
  1604. namemod.mod_type = const_cast<WCHAR *>(pwszAttribute);
  1605. namemod.mod_values = ppwszNew;
  1606. ppwszNew[iname++] = pwszNew;
  1607. ppwszNew[iname] = NULL;
  1608. hr = ldap_modify_ext_s(
  1609. pld,
  1610. const_cast<WCHAR *>(pwszDN),
  1611. mods,
  1612. NULL,
  1613. NULL);
  1614. *pdwDisposition = hr;
  1615. if (hr != S_OK)
  1616. {
  1617. hr = myHLdapError(pld, hr, ppwszError);
  1618. _JumpError(hr, error, "ldap_modify_ext_s");
  1619. }
  1620. }
  1621. hr = S_OK;
  1622. error:
  1623. if (NULL != pwszNew)
  1624. {
  1625. LocalFree(pwszNew);
  1626. }
  1627. if (NULL != ppwszNew)
  1628. {
  1629. LocalFree(ppwszNew);
  1630. }
  1631. return(hr);
  1632. }
  1633. HRESULT
  1634. myHLdapError3(
  1635. OPTIONAL IN LDAP *pld,
  1636. IN ULONG ldaperrParm,
  1637. IN ULONG ldaperrParmQuiet,
  1638. IN ULONG ldaperrParmQuiet2,
  1639. OPTIONAL OUT WCHAR **ppwszError)
  1640. {
  1641. HRESULT hr = S_OK;
  1642. if (NULL != ppwszError)
  1643. {
  1644. *ppwszError = NULL;
  1645. }
  1646. if (LDAP_SUCCESS != ldaperrParm)
  1647. {
  1648. BOOL fXlat = TRUE;
  1649. ULONG ldaperr;
  1650. WCHAR *pwszError = NULL;
  1651. if (NULL != pld)
  1652. {
  1653. ldaperr = ldap_get_option(pld, LDAP_OPT_SERVER_ERROR, &pwszError);
  1654. if (LDAP_SUCCESS != ldaperr)
  1655. {
  1656. _PrintError(ldaperr, "ldap_get_option(server error)");
  1657. pwszError = NULL;
  1658. }
  1659. ldaperr = ldap_get_option(pld, LDAP_OPT_SERVER_EXT_ERROR, &hr);
  1660. if (LDAP_SUCCESS != ldaperr)
  1661. {
  1662. _PrintError2(
  1663. ldaperr,
  1664. "ldap_get_option(server extended error)",
  1665. ldaperr);
  1666. }
  1667. else
  1668. {
  1669. fXlat = FALSE;
  1670. }
  1671. }
  1672. if (fXlat)
  1673. {
  1674. #undef LdapMapErrorToWin32
  1675. hr = LdapMapErrorToWin32(ldaperrParm);
  1676. #define LdapMapErrorToWin32 Use_myHLdapError_Instead_Of_LdapMapErrorToWin32
  1677. }
  1678. hr = myHError(hr);
  1679. _PrintErrorStr3(
  1680. ldaperrParm,
  1681. "ldaperr",
  1682. pwszError,
  1683. ldaperrParmQuiet,
  1684. ldaperrParmQuiet2);
  1685. if (NULL != ppwszError && NULL != pwszError)
  1686. {
  1687. WCHAR awc[32];
  1688. DWORD cwc;
  1689. wsprintf(awc, L"ldap: 0x%x: ", ldaperrParm);
  1690. cwc = wcslen(awc) + wcslen(pwszError);
  1691. *ppwszError = (WCHAR *) LocalAlloc(
  1692. LMEM_FIXED,
  1693. (cwc + 1) * sizeof(WCHAR));
  1694. if (NULL == *ppwszError)
  1695. {
  1696. _PrintError(E_OUTOFMEMORY, "LocalAlloc");
  1697. }
  1698. else
  1699. {
  1700. wcscpy(*ppwszError, awc);
  1701. wcscat(*ppwszError, pwszError);
  1702. }
  1703. }
  1704. if(NULL != pwszError)
  1705. ldap_memfree(pwszError);
  1706. }
  1707. return(hr);
  1708. }
  1709. HRESULT
  1710. myHLdapError2(
  1711. OPTIONAL IN LDAP *pld,
  1712. IN ULONG ldaperrParm,
  1713. IN ULONG ldaperrParmQuiet,
  1714. OPTIONAL OUT WCHAR **ppwszError)
  1715. {
  1716. return(myHLdapError3(
  1717. pld,
  1718. ldaperrParm,
  1719. ldaperrParmQuiet,
  1720. LDAP_SUCCESS,
  1721. ppwszError));
  1722. }
  1723. HRESULT
  1724. myHLdapError(
  1725. OPTIONAL IN LDAP *pld,
  1726. IN ULONG ldaperrParm,
  1727. OPTIONAL OUT WCHAR **ppwszError)
  1728. {
  1729. return(myHLdapError3(
  1730. pld,
  1731. ldaperrParm,
  1732. LDAP_SUCCESS,
  1733. LDAP_SUCCESS,
  1734. ppwszError));
  1735. }
  1736. HRESULT
  1737. myHLdapLastError(
  1738. OPTIONAL IN LDAP *pld,
  1739. OPTIONAL OUT WCHAR **ppwszError)
  1740. {
  1741. return(myHLdapError3(
  1742. pld,
  1743. LdapGetLastError(),
  1744. LDAP_SUCCESS,
  1745. LDAP_SUCCESS,
  1746. ppwszError));
  1747. }
  1748. HRESULT myLDAPSetStringAttribute(
  1749. IN LDAP *pld,
  1750. IN WCHAR const *pwszDN,
  1751. IN WCHAR const *pwszAttribute,
  1752. IN WCHAR const *pwszValue,
  1753. OUT DWORD *pdwDisposition,
  1754. OPTIONAL OUT WCHAR **ppwszError)
  1755. {
  1756. HRESULT hr = S_OK;
  1757. LDAPMod *mods[2];
  1758. LDAPMod certmod;
  1759. const WCHAR *ppwszVals[2];
  1760. CAutoLPWSTR pwszDNOnly;
  1761. WCHAR *pwszSuffix; // no free
  1762. hr = TrimURLDN(pwszDN, &pwszSuffix, &pwszDNOnly);
  1763. _JumpIfErrorStr(hr, error, "TrimURLDN", pwszDN);
  1764. mods[0] = &certmod;
  1765. mods[1] = NULL;
  1766. ppwszVals[0] = pwszValue;
  1767. ppwszVals[1] = NULL;
  1768. certmod.mod_op = LDAP_MOD_REPLACE;
  1769. certmod.mod_type = const_cast<WCHAR *>(pwszAttribute);
  1770. certmod.mod_values = const_cast<PWCHAR *>(ppwszVals);
  1771. hr = ldap_modify_ext_s(
  1772. pld,
  1773. pwszDNOnly,
  1774. mods,
  1775. NULL,
  1776. NULL);
  1777. *pdwDisposition = hr;
  1778. if (hr != S_OK)
  1779. {
  1780. hr = myHLdapError(pld, hr, ppwszError);
  1781. _JumpError(hr, error, "ldap_modify_ext_s");
  1782. }
  1783. hr = S_OK;
  1784. error:
  1785. return hr;
  1786. }