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.

1079 lines
27 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: tinstall.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <windows.h>
  13. #include <stdio.h>
  14. #include <certca.h>
  15. #include <winldap.h>
  16. #include <ntldap.h>
  17. #include <dsrole.h>
  18. #include <dsgetdc.h>
  19. #include <accctrl.h>
  20. #include <lmerr.h>
  21. #include <lmaccess.h>
  22. #include <lmapibuf.h>
  23. //--------------------------------------------------------------------------
  24. //
  25. // Defines
  26. //
  27. //--------------------------------------------------------------------------
  28. #define DS_RETEST_SECONDS 3
  29. #define CVT_BASE (1000 * 1000 * 10)
  30. #define CVT_SECONDS (1)
  31. #define CERTTYPE_SECURITY_DESCRIPTOR_NAME L"NTSecurityDescriptor"
  32. #define TEMPLATE_CONTAINER_NAME L"CN=Certificate Templates,CN=Public Key Services,CN=Services,"
  33. #define SCHEMA_CONTAINER_NAME L"CN=Schema,"
  34. typedef WCHAR *CERTSTR;
  35. //--------------------------------------------------------------------------
  36. //
  37. //
  38. // Helper Functions
  39. //
  40. //--------------------------------------------------------------------------
  41. HANDLE GetClientIdentity()
  42. {
  43. HANDLE hHandle = NULL;
  44. HANDLE hClientToken = NULL;
  45. HANDLE hProcessToken = NULL;
  46. // Step 1: attempt to acquire the thread token.
  47. if(hHandle = GetCurrentThread())
  48. {
  49. if (OpenThreadToken(hHandle,
  50. TOKEN_QUERY,
  51. TRUE, // open as self
  52. &hClientToken))
  53. goto Exit;
  54. }
  55. if (hHandle != NULL)
  56. {
  57. CloseHandle(hHandle);
  58. hHandle=NULL;
  59. }
  60. // We failed to get the thread token, now try to acquire the process token:
  61. hHandle = GetCurrentProcess();
  62. if (NULL == hHandle)
  63. goto Exit;
  64. if (!OpenProcessToken(hHandle,
  65. TOKEN_DUPLICATE,
  66. &hProcessToken))
  67. goto Exit;
  68. if(!DuplicateToken(hProcessToken,
  69. SecurityImpersonation,
  70. &hClientToken))
  71. goto Exit;
  72. Exit:
  73. if (NULL != hHandle)
  74. CloseHandle(hHandle);
  75. if (NULL != hProcessToken)
  76. CloseHandle(hProcessToken);
  77. return hClientToken;
  78. }
  79. HRESULT
  80. myHError(HRESULT hr)
  81. {
  82. if (S_OK != hr && S_FALSE != hr && !FAILED(hr))
  83. {
  84. hr = HRESULT_FROM_WIN32(hr);
  85. if (0 == HRESULT_CODE(hr))
  86. {
  87. // A call failed without properly setting an error condition!
  88. hr = E_UNEXPECTED;
  89. }
  90. }
  91. return(hr);
  92. }
  93. HRESULT
  94. myDupString(
  95. IN WCHAR const *pwszIn,
  96. IN WCHAR **ppwszOut)
  97. {
  98. DWORD cb;
  99. HRESULT hr;
  100. cb = (wcslen(pwszIn) + 1) * sizeof(WCHAR);
  101. *ppwszOut = (WCHAR *) LocalAlloc(LMEM_FIXED, cb);
  102. if (NULL == *ppwszOut)
  103. {
  104. hr = E_OUTOFMEMORY;
  105. goto error;
  106. }
  107. CopyMemory(*ppwszOut, pwszIn, cb);
  108. hr = S_OK;
  109. error:
  110. return(hr);
  111. }
  112. UINT CertStringLen(CERTSTR cstrString)
  113. {
  114. if(cstrString == NULL)
  115. {
  116. return 0;
  117. }
  118. return(*((UINT *)((PBYTE)cstrString - sizeof(UINT))))/sizeof(WCHAR);
  119. }
  120. UINT CertStringByteLen(CERTSTR cstrString)
  121. {
  122. if(cstrString == NULL)
  123. {
  124. return 0;
  125. }
  126. return(*((UINT *)((PBYTE)cstrString - sizeof(UINT))));
  127. }
  128. CERTSTR CertAllocStringByteLen(LPCSTR szString, UINT len)
  129. {
  130. PBYTE pbResult;
  131. pbResult = (PBYTE)LocalAlloc(LMEM_FIXED, len + sizeof(UINT) + sizeof(WCHAR));
  132. if (NULL == pbResult)
  133. return NULL;
  134. *((UINT *)pbResult) = len;
  135. pbResult += sizeof(UINT);
  136. *((UNALIGNED WCHAR *)(pbResult+len)) = 0;
  137. if(szString)
  138. {
  139. CopyMemory(pbResult, szString, min(len, strlen(szString)+1));
  140. }
  141. return (CERTSTR)pbResult;
  142. }
  143. CERTSTR CertAllocStringLen(LPCWSTR wszString, UINT len)
  144. {
  145. CERTSTR szResult;
  146. szResult = CertAllocStringByteLen(NULL, len*sizeof(WCHAR));
  147. if (NULL != szResult && NULL != wszString)
  148. {
  149. CopyMemory(szResult, wszString, min(wcslen(wszString)+1, len)*sizeof(WCHAR));
  150. }
  151. return szResult;
  152. }
  153. HRESULT CertFreeString(CERTSTR cstrString)
  154. {
  155. WCHAR *pData = (WCHAR *)(((PBYTE)cstrString)-sizeof(UINT));
  156. if(cstrString == NULL)
  157. {
  158. return E_POINTER;
  159. }
  160. LocalFree(pData);
  161. return S_OK;
  162. }
  163. CERTSTR CertAllocString(LPCWSTR wszString)
  164. {
  165. if(wszString == NULL)
  166. {
  167. return NULL;
  168. }
  169. return CertAllocStringLen(wszString, wcslen(wszString)+1);
  170. }
  171. DWORD
  172. CAGetAuthoritativeDomainDn(
  173. IN LDAP* LdapHandle,
  174. OUT CERTSTR *DomainDn,
  175. OUT CERTSTR *ConfigDn
  176. )
  177. /*++
  178. Routine Description:
  179. This routine simply queries the operational attributes for the
  180. domaindn and configdn.
  181. The strings returned by this routine must be freed by the caller
  182. using RtlFreeHeap() using the process heap.
  183. Parameters:
  184. LdapHandle : a valid handle to an ldap session
  185. DomainDn : a pointer to a string to be allocated in this routine
  186. ConfigDn : a pointer to a string to be allocated in this routine
  187. Return Values:
  188. An error from the win32 error space.
  189. ERROR_SUCCESS and
  190. Other operation errors.
  191. --*/
  192. {
  193. DWORD WinError = ERROR_SUCCESS;
  194. ULONG LdapError;
  195. LDAPMessage *SearchResult = NULL;
  196. LDAPMessage *Entry = NULL;
  197. WCHAR *Attr = NULL;
  198. BerElement *BerElement;
  199. WCHAR **Values = NULL;
  200. WCHAR *AttrArray[3];
  201. WCHAR *DefaultNamingContext = L"defaultNamingContext";
  202. WCHAR *ConfigNamingContext = L"configurationNamingContext";
  203. WCHAR *ObjectClassFilter = L"objectClass=*";
  204. //
  205. // These must be present
  206. //
  207. //
  208. // Set the out parameters to null
  209. //
  210. if(DomainDn)
  211. {
  212. *DomainDn = NULL;
  213. }
  214. if(ConfigDn)
  215. {
  216. *ConfigDn = NULL;
  217. }
  218. //
  219. // Query for the ldap server oerational attributes to obtain the default
  220. // naming context.
  221. //
  222. AttrArray[0] = DefaultNamingContext;
  223. AttrArray[1] = ConfigNamingContext; // this is the sentinel
  224. AttrArray[2] = NULL; // this is the sentinel
  225. __try
  226. {
  227. LdapError = ldap_search_sW(LdapHandle,
  228. NULL,
  229. LDAP_SCOPE_BASE,
  230. ObjectClassFilter,
  231. AttrArray,
  232. FALSE,
  233. &SearchResult);
  234. WinError = LdapMapErrorToWin32(LdapError);
  235. if (ERROR_SUCCESS == WinError) {
  236. Entry = ldap_first_entry(LdapHandle, SearchResult);
  237. if (Entry)
  238. {
  239. Attr = ldap_first_attributeW(LdapHandle, Entry, &BerElement);
  240. while (Attr)
  241. {
  242. if (!_wcsicmp(Attr, DefaultNamingContext))
  243. {
  244. if(DomainDn)
  245. {
  246. Values = ldap_get_values(LdapHandle, Entry, Attr);
  247. if (Values && Values[0])
  248. {
  249. (*DomainDn) = CertAllocString(Values[0]);
  250. }
  251. ldap_value_free(Values);
  252. }
  253. }
  254. else if (!_wcsicmp(Attr, ConfigNamingContext))
  255. {
  256. if(ConfigDn)
  257. {
  258. Values = ldap_get_values(LdapHandle, Entry, Attr);
  259. if (Values && Values[0])
  260. {
  261. (*ConfigDn) = CertAllocString(Values[0]);
  262. }
  263. ldap_value_free(Values);
  264. }
  265. }
  266. Attr = ldap_next_attribute(LdapHandle, Entry, BerElement);
  267. }
  268. }
  269. if ( DomainDn && (!(*DomainDn)))
  270. {
  271. //
  272. // We could get the default domain - bail out
  273. //
  274. WinError = ERROR_CANT_ACCESS_DOMAIN_INFO;
  275. }
  276. else if ( ConfigDn && (!(*ConfigDn)))
  277. {
  278. //
  279. // We could get the default domain - bail out
  280. //
  281. WinError = ERROR_CANT_ACCESS_DOMAIN_INFO;
  282. }
  283. }
  284. }
  285. __except(WinError = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER)
  286. {
  287. }
  288. // make sure we free this
  289. if (SearchResult)
  290. ldap_msgfree( SearchResult );
  291. return WinError;
  292. }
  293. HRESULT
  294. myDoesDSExist(
  295. IN BOOL fRetry)
  296. {
  297. HRESULT hr = S_OK;
  298. static BOOL s_fKnowDSExists = FALSE;
  299. static HRESULT s_hrDSExists = HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN);
  300. static FILETIME s_ftNextTest = {0,0};
  301. if (s_fKnowDSExists && (s_hrDSExists != S_OK) && fRetry)
  302. // s_fKnowDSExists = FALSE; // force a retry
  303. {
  304. FILETIME ftCurrent;
  305. GetSystemTimeAsFileTime(&ftCurrent);
  306. // if Compare is < 0 (next < current), force retest
  307. if (0 > CompareFileTime(&s_ftNextTest, &ftCurrent))
  308. s_fKnowDSExists = FALSE;
  309. }
  310. if (!s_fKnowDSExists)
  311. {
  312. GetSystemTimeAsFileTime(&s_ftNextTest);
  313. // set NEXT in 100ns increments
  314. ((LARGE_INTEGER *) &s_ftNextTest)->QuadPart +=
  315. (__int64) (CVT_BASE * CVT_SECONDS * 60) * DS_RETEST_SECONDS;
  316. // NetApi32 is delay loaded, so wrap to catch problems when it's not available
  317. __try
  318. {
  319. DOMAIN_CONTROLLER_INFO *pDCI;
  320. DSROLE_PRIMARY_DOMAIN_INFO_BASIC *pDsRole;
  321. // ensure we're not standalone
  322. pDsRole = NULL;
  323. hr = DsRoleGetPrimaryDomainInformation( // Delayload wrapped
  324. NULL,
  325. DsRolePrimaryDomainInfoBasic,
  326. (BYTE **) &pDsRole);
  327. if (S_OK == hr &&
  328. (pDsRole->MachineRole == DsRole_RoleStandaloneServer ||
  329. pDsRole->MachineRole == DsRole_RoleStandaloneWorkstation))
  330. {
  331. hr = HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN);
  332. }
  333. if (NULL != pDsRole)
  334. {
  335. DsRoleFreeMemory(pDsRole); // Delayload wrapped
  336. }
  337. if (S_OK == hr)
  338. {
  339. // not standalone; return info on our DS
  340. pDCI = NULL;
  341. hr = DsGetDcName( // Delayload wrapped
  342. NULL,
  343. NULL,
  344. NULL,
  345. NULL,
  346. DS_DIRECTORY_SERVICE_PREFERRED,
  347. &pDCI);
  348. if (S_OK == hr && 0 == (pDCI->Flags & DS_DS_FLAG))
  349. {
  350. hr = HRESULT_FROM_WIN32(ERROR_CANT_ACCESS_DOMAIN_INFO);
  351. }
  352. if (NULL != pDCI)
  353. {
  354. NetApiBufferFree(pDCI); // Delayload wrapped
  355. }
  356. }
  357. s_fKnowDSExists = TRUE;
  358. }
  359. __except(hr = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER)
  360. {
  361. }
  362. // else just allow users without netapi flounder with timeouts
  363. // if ds not available...
  364. s_hrDSExists = myHError(hr);
  365. }
  366. return(s_hrDSExists);
  367. }
  368. HRESULT
  369. myRobustLdapBindEx(
  370. OUT LDAP ** ppldap,
  371. OPTIONAL OUT LPWSTR* ppszForestDNSName,
  372. IN BOOL fGC)
  373. {
  374. HRESULT hr;
  375. BOOL fForceRediscovery = FALSE;
  376. DWORD dwGetDCFlags = DS_RETURN_DNS_NAME;
  377. PDOMAIN_CONTROLLER_INFO pDomainInfo = NULL;
  378. LDAP *pld = NULL;
  379. WCHAR const *pwszDomainControllerName = NULL;
  380. ULONG ldaperr;
  381. if (fGC)
  382. {
  383. dwGetDCFlags |= DS_GC_SERVER_REQUIRED;
  384. }
  385. do {
  386. if (fForceRediscovery)
  387. {
  388. dwGetDCFlags |= DS_FORCE_REDISCOVERY;
  389. }
  390. ldaperr = LDAP_SERVER_DOWN;
  391. // netapi32!DsGetDcName is delay loaded, so wrap
  392. __try
  393. {
  394. // Get the GC location
  395. hr = DsGetDcName(
  396. NULL, // Delayload wrapped
  397. NULL,
  398. NULL,
  399. NULL,
  400. dwGetDCFlags,
  401. &pDomainInfo);
  402. }
  403. __except(hr = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER)
  404. {
  405. }
  406. if (S_OK != hr)
  407. {
  408. hr = HRESULT_FROM_WIN32(hr);
  409. if (fForceRediscovery)
  410. {
  411. goto error;
  412. }
  413. fForceRediscovery = TRUE;
  414. continue;
  415. }
  416. if (NULL == pDomainInfo ||
  417. (fGC && 0 == (DS_GC_FLAG & pDomainInfo->Flags)) ||
  418. 0 == (DS_DNS_CONTROLLER_FLAG & pDomainInfo->Flags) ||
  419. NULL == pDomainInfo->DomainControllerName)
  420. {
  421. if (!fForceRediscovery)
  422. {
  423. fForceRediscovery = TRUE;
  424. continue;
  425. }
  426. hr = HRESULT_FROM_WIN32(ERROR_CANT_ACCESS_DOMAIN_INFO);
  427. goto error;
  428. }
  429. pwszDomainControllerName = pDomainInfo->DomainControllerName;
  430. // skip past forward slashes (why are they there?)
  431. while (L'\\' == *pwszDomainControllerName)
  432. {
  433. pwszDomainControllerName++;
  434. }
  435. // bind to ds
  436. pld = ldap_init(
  437. const_cast<WCHAR *>(pwszDomainControllerName),
  438. fGC? LDAP_GC_PORT : LDAP_PORT);
  439. if (NULL == pld)
  440. {
  441. ldaperr = LdapGetLastError();
  442. }
  443. else
  444. {
  445. // do this because we're explicitly setting DC name
  446. ldaperr = ldap_set_option(pld, LDAP_OPT_AREC_EXCLUSIVE, LDAP_OPT_ON);
  447. ldaperr = ldap_bind_s(pld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  448. }
  449. hr = myHError(LdapMapErrorToWin32(ldaperr));
  450. if (fForceRediscovery)
  451. {
  452. break;
  453. }
  454. fForceRediscovery = TRUE;
  455. } while (LDAP_SERVER_DOWN == ldaperr);
  456. // everything's cool, party down
  457. if (S_OK == hr)
  458. {
  459. if (NULL != ppszForestDNSName)
  460. {
  461. hr = myDupString(
  462. pDomainInfo->DomainControllerName,
  463. ppszForestDNSName);
  464. if(S_OK != hr)
  465. goto error;
  466. }
  467. *ppldap = pld;
  468. pld = NULL;
  469. }
  470. error:
  471. if (NULL != pld)
  472. {
  473. ldap_unbind(pld);
  474. }
  475. // we know netapi32 was already loaded safely (that's where we got
  476. // pDomainInfo), so no need to wrap
  477. if (NULL != pDomainInfo)
  478. {
  479. NetApiBufferFree(pDomainInfo); // Delayload wrapped
  480. }
  481. return(hr);
  482. }
  483. HRESULT
  484. myRobustLdapBind(
  485. OUT LDAP ** ppldap,
  486. IN BOOL fGC)
  487. {
  488. return(myRobustLdapBindEx(ppldap, NULL, fGC));
  489. }
  490. //--------------------------------------------------------------------
  491. static HRESULT GetRootDomEntitySid(SID ** ppSid, DWORD dwEntityRid)
  492. {
  493. HRESULT hr;
  494. NET_API_STATUS nasError;
  495. unsigned int nSubAuthorities;
  496. unsigned int nSubAuthIndex;
  497. // must be cleaned up
  498. SID * psidRootDomEntity=NULL;
  499. USER_MODALS_INFO_2 * pumi2=NULL;
  500. DOMAIN_CONTROLLER_INFOW * pdci=NULL;
  501. DOMAIN_CONTROLLER_INFOW * pdciForest=NULL;
  502. // initialize out params
  503. *ppSid=NULL;
  504. // get the forest name
  505. nasError=DsGetDcNameW(NULL, NULL, NULL, NULL, 0, &pdciForest);
  506. if (NERR_Success!=nasError) {
  507. hr=HRESULT_FROM_WIN32(nasError);
  508. goto error;
  509. }
  510. // get the top level DC name
  511. nasError=DsGetDcNameW(NULL, pdciForest->DnsForestName, NULL, NULL, 0, &pdci);
  512. if (NERR_Success!=nasError) {
  513. hr=HRESULT_FROM_WIN32(nasError);
  514. goto error;
  515. }
  516. // get the domain Sid on the top level DC.
  517. nasError=NetUserModalsGet(pdci->DomainControllerName, 2, (LPBYTE *)&pumi2);
  518. if(NERR_Success!=nasError) {
  519. hr=HRESULT_FROM_WIN32(nasError);
  520. goto error;
  521. }
  522. nSubAuthorities=*GetSidSubAuthorityCount(pumi2->usrmod2_domain_id);
  523. // allocate storage for new Sid. account domain Sid + account Rid
  524. psidRootDomEntity=(SID *)LocalAlloc(LPTR, GetSidLengthRequired((UCHAR)(nSubAuthorities+1)));
  525. if(NULL == psidRootDomEntity)
  526. {
  527. hr=E_OUTOFMEMORY;
  528. goto error;
  529. }
  530. // copy the first few peices into the SID
  531. if (!InitializeSid(psidRootDomEntity,
  532. GetSidIdentifierAuthority(pumi2->usrmod2_domain_id),
  533. (BYTE)(nSubAuthorities+1)))
  534. {
  535. hr=HRESULT_FROM_WIN32(GetLastError());
  536. goto error;
  537. }
  538. // copy existing subauthorities from account domain Sid into new Sid
  539. for (nSubAuthIndex=0; nSubAuthIndex < nSubAuthorities ; nSubAuthIndex++) {
  540. *GetSidSubAuthority(psidRootDomEntity, nSubAuthIndex)=
  541. *GetSidSubAuthority(pumi2->usrmod2_domain_id, nSubAuthIndex);
  542. }
  543. // append Rid to new Sid
  544. *GetSidSubAuthority(psidRootDomEntity, nSubAuthorities)=dwEntityRid;
  545. *ppSid=psidRootDomEntity;
  546. psidRootDomEntity=NULL;
  547. hr=S_OK;
  548. error:
  549. if (NULL!=psidRootDomEntity) {
  550. FreeSid(psidRootDomEntity);
  551. }
  552. if (NULL!=pdci) {
  553. NetApiBufferFree(pdci);
  554. }
  555. if (NULL!=pdci) {
  556. NetApiBufferFree(pdciForest);
  557. }
  558. if (NULL!=pumi2) {
  559. NetApiBufferFree(pumi2);
  560. }
  561. return hr;
  562. }
  563. //--------------------------------------------------------------------
  564. HRESULT GetRootDomAdminSid(SID ** ppSid)
  565. {
  566. return GetRootDomEntitySid(ppSid, DOMAIN_GROUP_RID_ADMINS);
  567. }
  568. //***********************************************************************************
  569. //
  570. //
  571. // Main
  572. //
  573. // This function will install new Whistler certificate template if and onlyif
  574. // the following conditions are TRUE:
  575. //
  576. // 1. Whilster Schema
  577. // 2. New certificate templates have not yet installed
  578. // 3. The caller has privilege to install templates in the directory
  579. //
  580. //
  581. //***********************************************************************************
  582. void __cdecl main(
  583. int argc,
  584. char **argv)
  585. {
  586. HRESULT hr=S_OK;
  587. DWORD dwErr=0;
  588. ULONG ldaperr=0;
  589. struct l_timeval timeout;
  590. DWORD dwCount=0;
  591. LPWSTR awszAttr[2];
  592. BOOL fAccessAllowed = FALSE;
  593. DWORD grantAccess=0;
  594. GENERIC_MAPPING AccessMapping;
  595. PRIVILEGE_SET ps;
  596. DWORD dwPSSize = sizeof(ps);
  597. LDAPMessage *Entry = NULL;
  598. CHAR sdBerValue[] = {0x30, 0x03, 0x02, 0x01, DACL_SECURITY_INFORMATION |
  599. OWNER_SECURITY_INFORMATION |
  600. GROUP_SECURITY_INFORMATION };
  601. LDAPControl se_info_control =
  602. {
  603. LDAP_SERVER_SD_FLAGS_OID_W,
  604. {
  605. 5, sdBerValue
  606. },
  607. TRUE
  608. };
  609. LDAPControl permissive_modify_control =
  610. {
  611. LDAP_SERVER_PERMISSIVE_MODIFY_OID_W,
  612. {
  613. 0, NULL
  614. },
  615. FALSE
  616. };
  617. PLDAPControl server_controls[3] =
  618. {
  619. &se_info_control,
  620. &permissive_modify_control,
  621. NULL
  622. };
  623. HCERTTYPE hCertType=NULL;
  624. LDAP *pld = NULL;
  625. CERTSTR bstrConfig = NULL;
  626. CERTSTR bstrDN = NULL;
  627. LDAPMessage *SearchResult = NULL;
  628. LDAPMessage *SDResult = NULL;
  629. struct berval **apSD =NULL;
  630. PSECURITY_DESCRIPTOR pSD=NULL;
  631. HANDLE hClientToken=NULL;
  632. SID * psidRootDomAdmins=NULL;
  633. BOOL bIsRootDomAdmin=FALSE;
  634. //*************************************************************
  635. //
  636. // check the schema version
  637. //
  638. //retrieve the ldap handle and the config string
  639. if(S_OK != myDoesDSExist(TRUE))
  640. {
  641. wprintf(L"No DS exists.\n");
  642. goto error;
  643. }
  644. if(S_OK != (hr = myRobustLdapBind(&pld, FALSE)))
  645. {
  646. wprintf(L"Error: Failed to bind to the DS.\n");
  647. goto error;
  648. }
  649. dwErr = CAGetAuthoritativeDomainDn(pld, NULL, &bstrConfig);
  650. if(ERROR_SUCCESS != dwErr)
  651. {
  652. wprintf(L"Error: Failed to get the domain name.\n");
  653. hr = HRESULT_FROM_WIN32(dwErr);
  654. goto error;
  655. }
  656. bstrDN = CertAllocStringLen(NULL, wcslen(bstrConfig) + wcslen(SCHEMA_CONTAINER_NAME));
  657. if(NULL == bstrDN)
  658. {
  659. wprintf(L"Error: Failed to get the container name.\n");
  660. hr=E_OUTOFMEMORY;
  661. goto error;
  662. }
  663. wcscpy(bstrDN, SCHEMA_CONTAINER_NAME);
  664. wcscat(bstrDN, bstrConfig);
  665. timeout.tv_sec = 300;
  666. timeout.tv_usec = 0;
  667. awszAttr[0]=L"cn";
  668. awszAttr[1]=NULL;
  669. ldaperr = ldap_search_stW(
  670. pld,
  671. (LPWSTR)bstrDN,
  672. LDAP_SCOPE_ONELEVEL,
  673. L"(cn=ms-PKI-Enrollment-Flag)",
  674. awszAttr,
  675. 0,
  676. &timeout,
  677. &SearchResult);
  678. if(LDAP_SUCCESS != ldaperr)
  679. {
  680. wprintf(L"We have W2K Schema. Exit\n");
  681. hr=S_OK;
  682. goto error;
  683. }
  684. dwCount = ldap_count_entries(pld, SearchResult);
  685. if(0 == dwCount)
  686. {
  687. wprintf(L"We have W2K Schema. Exit\n");
  688. hr=S_OK;
  689. goto error;
  690. }
  691. //*************************************************************
  692. //
  693. // check if keyRecoveryAgent certificate is present and
  694. // and update to date
  695. //
  696. hr=CAFindCertTypeByName(
  697. wszCERTTYPE_DS_EMAIL_REPLICATION,
  698. NULL,
  699. CT_ENUM_MACHINE_TYPES | CT_FLAG_NO_CACHE_LOOKUP,
  700. &hCertType);
  701. if((S_OK == hr) && (NULL!=hCertType))
  702. {
  703. wprintf(L"Key Recovery Agent certificate template already exists.\n");
  704. //check if the template is update to date
  705. if(CAIsCertTypeCurrent(0, NULL))
  706. {
  707. wprintf(L"All certificate templates are current. Exit\n");
  708. goto error;
  709. }
  710. }
  711. //*************************************************************
  712. //
  713. // check the write access
  714. //
  715. //
  716. if(NULL==(hClientToken=GetClientIdentity()))
  717. {
  718. wprintf(L"Can not retrieve client identity.\n");
  719. hr = myHError(GetLastError());
  720. goto error;
  721. }
  722. //get the SD of the certificate template container
  723. if(bstrDN)
  724. CertFreeString(bstrDN);
  725. bstrDN = CertAllocStringLen(NULL, wcslen(bstrConfig) + wcslen(TEMPLATE_CONTAINER_NAME));
  726. if(NULL == bstrDN)
  727. {
  728. wprintf(L"Error: Failed to get the container name.\n");
  729. hr=E_OUTOFMEMORY;
  730. goto error;
  731. }
  732. wcscpy(bstrDN, TEMPLATE_CONTAINER_NAME);
  733. wcscat(bstrDN, bstrConfig);
  734. awszAttr[0]=CERTTYPE_SECURITY_DESCRIPTOR_NAME;
  735. awszAttr[1]=NULL;
  736. ldaperr = ldap_search_ext_sW(
  737. pld,
  738. (LPWSTR)bstrDN,
  739. LDAP_SCOPE_BASE,
  740. L"(objectclass=*)",
  741. awszAttr,
  742. 0,
  743. (PLDAPControl *)&server_controls,
  744. NULL,
  745. &timeout,
  746. 10,
  747. &SDResult);
  748. if(LDAP_SUCCESS != ldaperr)
  749. {
  750. wprintf(L"Fail to locate the template container.\n");
  751. hr = myHError(LdapMapErrorToWin32(ldaperr));
  752. goto error;
  753. }
  754. if(NULL == (Entry = ldap_first_entry(pld, SDResult)))
  755. {
  756. wprintf(L"Can not find first entry for template container.\n");
  757. hr = E_UNEXPECTED;
  758. goto error;
  759. }
  760. apSD = ldap_get_values_len(pld, Entry, CERTTYPE_SECURITY_DESCRIPTOR_NAME);
  761. if(apSD == NULL)
  762. {
  763. wprintf(L"Can not retrieve security descriptor.\n");
  764. hr = E_FAIL;
  765. goto error;
  766. }
  767. pSD = LocalAlloc(LMEM_FIXED, (*apSD)->bv_len);
  768. if(pSD == NULL)
  769. {
  770. wprintf(L"Error: No more memory.\n");
  771. hr = E_OUTOFMEMORY;
  772. goto error;
  773. }
  774. CopyMemory(pSD, (*apSD)->bv_val, (*apSD)->bv_len);
  775. //check the write access
  776. if(!AccessCheckByType(
  777. pSD, // security descriptor
  778. NULL, // SID of object being checked
  779. hClientToken, // handle to client access token
  780. ACTRL_DS_CREATE_CHILD |
  781. ACTRL_DS_LIST, // requested access rights
  782. NULL, // array of object types
  783. 0, // number of object type elements
  784. &AccessMapping, // map generic to specific rights
  785. &ps, // receives privileges used
  786. &dwPSSize, // size of privilege-set buffer
  787. &grantAccess, // retrieves mask of granted rights
  788. &fAccessAllowed)) // retrieves results of access check);
  789. {
  790. wprintf(L"Error: Fail to check the write access.\n");
  791. hr = myHError(GetLastError());
  792. goto error;
  793. }
  794. if(!fAccessAllowed)
  795. {
  796. wprintf(L"No previlege to create certificate template. Exit\n");
  797. hr = S_OK;
  798. goto error;
  799. }
  800. //*************************************************************
  801. //
  802. // check the root domain admin rights
  803. //
  804. //
  805. hr=GetRootDomAdminSid(&psidRootDomAdmins);
  806. if(S_OK != hr)
  807. {
  808. wprintf(L"Error: GetRootDomAdminSid.\n");
  809. goto error;
  810. }
  811. // check for membership
  812. if (!CheckTokenMembership(NULL, psidRootDomAdmins, &bIsRootDomAdmin))
  813. {
  814. hr=HRESULT_FROM_WIN32(GetLastError());
  815. wprintf(L"Error: CheckTokenMembership.\n");
  816. goto error;
  817. }
  818. if(FALSE == bIsRootDomAdmin)
  819. {
  820. wprintf(L"No domain admin rights to create certificate template. Exit\n");
  821. hr = S_OK;
  822. goto error;
  823. }
  824. //NEED to check the enterpris admin rights. Bryan apparently have figured this out.
  825. //*************************************************************
  826. //
  827. // everything looks good. Install the certificate templates
  828. //
  829. //
  830. if(IDNO == MessageBoxW(
  831. NULL,
  832. L"The system detects that new certificate templates should be installed. Do you want to install them?",
  833. L"Certificate Templates",
  834. MB_YESNO))
  835. {
  836. hr=S_OK;
  837. goto error;
  838. }
  839. hr=CAInstallDefaultCertType(0);
  840. if(hr != S_OK)
  841. {
  842. wprintf(L"Installation failed with 0x%08X.\n", hr);
  843. goto error;
  844. }
  845. wprintf(L"Certificate templates are successfully installed.\n");
  846. error:
  847. if (psidRootDomAdmins)
  848. FreeSid(psidRootDomAdmins);
  849. if(hClientToken)
  850. CloseHandle(hClientToken);
  851. if(pSD)
  852. LocalFree(pSD);
  853. if(apSD != NULL)
  854. ldap_value_free_len(apSD);
  855. if(SDResult)
  856. ldap_msgfree(SDResult);
  857. if(SearchResult)
  858. ldap_msgfree(SearchResult);
  859. if(bstrDN)
  860. CertFreeString(bstrDN);
  861. if(hCertType)
  862. CACloseCertType(hCertType);
  863. if(bstrConfig)
  864. CertFreeString(bstrConfig);
  865. if (pld)
  866. ldap_unbind(pld);
  867. if(S_OK == hr)
  868. wprintf(L"Test Passed\n");
  869. else
  870. wprintf(L"Test Failed\n");
  871. }