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.

662 lines
20 KiB

  1. // AccessChecker.cpp : Implementation of CAccessChecker
  2. #include "stdafx.h"
  3. #include "WorkObj.h"
  4. #include "Checker.h"
  5. #include <lm.h>
  6. #include "GetDcName.h"
  7. #include <iads.h>
  8. #include <comdef.h>
  9. //#include <adshlp.h>
  10. #include "treg.hpp"
  11. #include "BkupRstr.hpp"
  12. #include "UString.hpp"
  13. #include "EaLen.hpp"
  14. #include "IsAdmin.hpp"
  15. #include <ntsecapi.h>
  16. #include <winerror.h>
  17. #include "SidHistoryFlags.h"
  18. #include "BkupRstr.hpp"
  19. #include "ResStr.h"
  20. //#import "\bin\NetEnum.tlb" no_namespace
  21. #import "NetEnum.tlb" no_namespace
  22. // Win2k function
  23. typedef HRESULT (CALLBACK * ADSGETOBJECT)(LPWSTR, REFIID, void**);
  24. /////////////////////////////////////////////////////////////////////////////
  25. // CAccessChecker
  26. STDMETHODIMP CAccessChecker::GetOsVersion(BSTR server, DWORD * pdwVerMaj, DWORD * pdwVerMin, DWORD * pdwVerSP)
  27. {
  28. // This function looksup the OS version on the server specified and returns it.
  29. // CAUTION : This function always returns 0 for the ServicePack.
  30. WKSTA_INFO_100 * pInfo;
  31. long rc = NetWkstaGetInfo(server,100,(LPBYTE*)&pInfo);
  32. if ( ! rc )
  33. {
  34. *pdwVerMaj = pInfo->wki100_ver_major;
  35. *pdwVerMin = pInfo->wki100_ver_minor;
  36. *pdwVerSP = 0;
  37. NetApiBufferFree(pInfo);
  38. }
  39. else
  40. return HRESULT_FROM_WIN32(rc);
  41. return S_OK;
  42. }
  43. STDMETHODIMP CAccessChecker::IsNativeMode(BSTR Domain, BOOL * pbIsNativeMode)
  44. {
  45. ADSGETOBJECT ADsGetObject;
  46. HMODULE hMod = LoadLibrary(L"activeds.dll");
  47. if ( hMod == NULL )
  48. return HRESULT_FROM_WIN32(GetLastError());
  49. ADsGetObject = (ADSGETOBJECT)GetProcAddress(hMod, "ADsGetObject");
  50. if (!ADsGetObject)
  51. {
  52. if ( hMod )
  53. FreeLibrary(hMod);
  54. return HRESULT_FROM_WIN32(GetLastError());
  55. }
  56. IADs * pDomain;
  57. HRESULT hr;
  58. VARIANT var;
  59. _bstr_t sDom( L"LDAP://" );
  60. sDom += Domain;
  61. hr = ADsGetObject(sDom, IID_IADs, (void **) &pDomain);
  62. if (SUCCEEDED(hr))
  63. {
  64. VariantInit(&var);
  65. //Get the ntMixedDomain attribute
  66. hr = pDomain->Get(L"ntMixedDomain", &var);
  67. if (SUCCEEDED(hr))
  68. {
  69. hr = E_FAIL;
  70. //Type should be VT_I4.
  71. if (var.vt==VT_I4)
  72. {
  73. //Zero means native mode.
  74. if (var.lVal == 0)
  75. {
  76. hr = S_OK;
  77. *pbIsNativeMode = true;
  78. }
  79. //One means mixed mode.
  80. else if(var.lVal == 1)
  81. {
  82. hr = S_OK;
  83. *pbIsNativeMode = false;
  84. }
  85. }
  86. }
  87. VariantClear(&var);
  88. pDomain->Release();
  89. }
  90. if ( hMod )
  91. FreeLibrary(hMod);
  92. return hr;
  93. }
  94. STDMETHODIMP CAccessChecker::CanUseAddSidHistory(BSTR srcDomain, BSTR tgtDomain, BSTR tgtDC, long * pbCanUseIt)
  95. {
  96. DWORD rc = 0; // OS return code
  97. WKSTA_INFO_100 * pInfo = NULL;
  98. TRegKey sysKey, regComputer;
  99. DWORD rval;
  100. _bstr_t bstrSourceMachine;
  101. _bstr_t bstrTargetMachine;
  102. // initialize the return FieldMask
  103. * pbCanUseIt = F_WORKS;
  104. rc = GetDcName4(srcDomain, DS_PDC_REQUIRED, bstrSourceMachine);
  105. if( rc != NO_ERROR ) goto ret_exit;
  106. if (tgtDC && *tgtDC)
  107. {
  108. bstrTargetMachine = tgtDC;
  109. }
  110. else
  111. {
  112. rc = GetDcName4(tgtDomain, 0, bstrTargetMachine);
  113. if( rc != NO_ERROR ) goto ret_exit;
  114. }
  115. if (GetBkupRstrPriv(bstrSourceMachine))
  116. {
  117. rc = regComputer.Connect( HKEY_LOCAL_MACHINE, bstrSourceMachine );
  118. // Check the registry to see if the TcpipClientSupport key is there
  119. if ( ! rc )
  120. {
  121. rc = sysKey.OpenRead(L"System\\CurrentControlSet\\Control\\Lsa",&regComputer);
  122. }
  123. if ( ! rc )
  124. {
  125. rc = sysKey.ValueGetDWORD(L"TcpipClientSupport",&rval);
  126. if ( !rc )
  127. {
  128. if ( rval != 1 )
  129. {
  130. *pbCanUseIt |= F_NO_REG_KEY;
  131. }
  132. }
  133. else
  134. {
  135. // DWORD value not found
  136. *pbCanUseIt |= F_NO_REG_KEY;
  137. rc = 0;
  138. }
  139. }
  140. }
  141. else
  142. {
  143. rc = GetLastError();
  144. }
  145. // Check if the target domain is a Win2k native mode domain.
  146. if ( !rc )
  147. {
  148. rc = NetWkstaGetInfo(bstrTargetMachine,100,(LPBYTE*)&pInfo);
  149. if ( ! rc )
  150. {
  151. if ( pInfo->wki100_ver_major < 5 )
  152. {
  153. // cannot add Sid history to non Win2k Domains
  154. *pbCanUseIt |= F_WRONGOS;
  155. }
  156. else{
  157. BOOL isNative = false;
  158. if(SUCCEEDED(IsNativeMode( _bstr_t(pInfo->wki100_langroup), &isNative))){
  159. if( isNative == false ) *pbCanUseIt |= F_WRONGOS;
  160. }
  161. }
  162. }
  163. else
  164. {
  165. rc = GetLastError();
  166. }
  167. }
  168. bool bDotNetOrLater = false;
  169. if (pInfo)
  170. {
  171. if (pInfo->wki100_ver_major > 5 || (pInfo->wki100_ver_major == 5 && pInfo->wki100_ver_minor > 0))
  172. {
  173. bDotNetOrLater = true;
  174. }
  175. }
  176. //
  177. // If the target domain controller is Windows 2000 then caller must
  178. // be a member of the domain administrators group in the target domain.
  179. //
  180. if (!rc)
  181. {
  182. if (!bDotNetOrLater)
  183. {
  184. PUSER_MODALS_INFO_2 pumi2Info;
  185. NET_API_STATUS nasStatus = NetUserModalsGet(bstrTargetMachine, 2, (LPBYTE*)&pumi2Info);
  186. if (nasStatus == NERR_Success)
  187. {
  188. rc = IsDomainAdmin(pumi2Info->usrmod2_domain_id);
  189. if (rc == ERROR_ACCESS_DENIED)
  190. {
  191. *pbCanUseIt |= F_NOT_DOMAIN_ADMIN;
  192. rc = ERROR_SUCCESS;
  193. }
  194. NetApiBufferFree(pumi2Info);
  195. }
  196. else
  197. {
  198. rc = nasStatus;
  199. }
  200. }
  201. }
  202. if ( !rc )
  203. {
  204. // Check auditing on the source domain.
  205. rc = DetectAuditing(bstrSourceMachine);
  206. if ( rc == -1 )
  207. {
  208. rc = 0;
  209. *pbCanUseIt |= F_NO_AUDITING_SOURCE;
  210. }
  211. }
  212. if ( !rc )
  213. {
  214. // Check auditing on the target domain.
  215. rc = DetectAuditing(bstrTargetMachine);
  216. if ( rc == -1 )
  217. {
  218. rc = 0;
  219. *pbCanUseIt |= F_NO_AUDITING_TARGET;
  220. }
  221. //
  222. // On .NET Server and later the permission to migrate SIDs may be granted to any user.
  223. // As this user may not be an administrator in the target domain it might not be
  224. // possible for this user to obtain access to auditing policy information. Therefore
  225. // an access denied error will be ignored so that the user may perform the SID
  226. // migration but only on .NET Server (5.1) or later. If Windows 2000 and the caller is
  227. // not a domain administrator then also set success as the reason for the access
  228. // denied has already been determined.
  229. //
  230. if (rc == ERROR_ACCESS_DENIED)
  231. {
  232. if (bDotNetOrLater || (*pbCanUseIt & F_NOT_DOMAIN_ADMIN))
  233. {
  234. rc = ERROR_SUCCESS;
  235. }
  236. }
  237. }
  238. if (!rc )
  239. {
  240. _bstr_t strDnsName;
  241. _bstr_t strFlatName;
  242. rc = GetDomainNames4(srcDomain, strFlatName, strDnsName);
  243. if (rc == ERROR_SUCCESS)
  244. {
  245. LOCALGROUP_INFO_0 * pInfo = NULL;
  246. WCHAR groupName[LEN_Account];
  247. wsprintf(groupName,L"%ls$$$",(WCHAR*)strFlatName);
  248. rc = NetLocalGroupGetInfo(bstrSourceMachine,groupName,0,(BYTE**)&pInfo);
  249. if ( rc == NERR_GroupNotFound )
  250. {
  251. rc = 0;
  252. *pbCanUseIt |= F_NO_LOCAL_GROUP;
  253. }
  254. else
  255. {
  256. NetApiBufferFree(pInfo);
  257. }
  258. }
  259. }
  260. ret_exit:
  261. if ( pInfo ) NetApiBufferFree(pInfo);
  262. return HRESULT_FROM_WIN32(rc);
  263. }
  264. //------------------------------------------------------------------------------------------
  265. // AddLocalGroup : Given the source domain, and source domain controller names, this
  266. // function creates the local group SOURCEDOMAIN$$$ in the source domain.
  267. // This local group must exist in the source domain for the DsAddSidHistory
  268. // API to work.
  269. //
  270. //------------------------------------------------------------------------------------------
  271. STDMETHODIMP CAccessChecker::AddLocalGroup(BSTR srcDomain, BSTR sourceDC)
  272. {
  273. DWORD rc = 0;
  274. LOCALGROUP_INFO_1 groupInfo;
  275. WCHAR name[LEN_Account];
  276. WCHAR comment[LEN_Account];
  277. DWORD parmErr;
  278. swprintf(name,L"%ls$$$",(WCHAR*)srcDomain);
  279. groupInfo.lgrpi1_name = name;
  280. wcscpy(comment, (WCHAR*)GET_BSTR(IDS_DOM_LOC_GRP_COMMENT));
  281. groupInfo.lgrpi1_comment = comment;
  282. rc = NetLocalGroupAdd(sourceDC,1,(LPBYTE)&groupInfo,&parmErr);
  283. return HRESULT_FROM_WIN32(rc);
  284. }
  285. //------------------------------------------------------------------------------------------
  286. // IsInSameForest : Given the source and the target domains this function tells us if
  287. // both the domains are in the same forest. This function enumerates all
  288. // the domains in the Forest of the source domain and compares them to
  289. // the target domain name. If there is a match then we know we are in same
  290. // forest.
  291. //------------------------------------------------------------------------------------------
  292. STDMETHODIMP CAccessChecker::IsInSameForest(BSTR srcDomain, BSTR tgtDomain, BOOL * pbIsSame)
  293. {
  294. // Initialize the return value
  295. *pbIsSame = FALSE;
  296. // Load the ADSI function dynamically
  297. ADSGETOBJECT ADsGetObject;
  298. HMODULE hMod = LoadLibrary(L"activeds.dll");
  299. if ( hMod == NULL )
  300. return HRESULT_FROM_WIN32(GetLastError());
  301. ADsGetObject = (ADSGETOBJECT)GetProcAddress(hMod, "ADsGetObject");
  302. if (!ADsGetObject)
  303. {
  304. if ( hMod )
  305. FreeLibrary(hMod);
  306. return HRESULT_FROM_WIN32(GetLastError());
  307. }
  308. // we are going to look up the Schema naming context of both domains.
  309. // if they are the same then these two domains are in same forest.
  310. IADs * pAds = NULL;
  311. HRESULT hr = S_OK;
  312. WCHAR sPath[LEN_Path];
  313. _variant_t var;
  314. _bstr_t srcSchema, tgtSchema;
  315. // Get the schemaNamingContext for the source domain.
  316. wsprintf(sPath, L"LDAP://%s/rootDSE", (WCHAR*) srcDomain);
  317. hr = ADsGetObject(sPath, IID_IADs, (void**) &pAds);
  318. if ( SUCCEEDED(hr) )
  319. hr = pAds->Get(L"schemaNamingContext", &var);
  320. if ( SUCCEEDED(hr) )
  321. srcSchema = var;
  322. else
  323. srcSchema = L"";
  324. if ( pAds )
  325. {
  326. pAds->Release();
  327. pAds = NULL;
  328. }
  329. if (SUCCEEDED(hr))
  330. {
  331. // Now do the same for the target domain.
  332. wsprintf(sPath, L"LDAP://%s/rootDSE", (WCHAR*) tgtDomain);
  333. hr = ADsGetObject(sPath, IID_IADs, (void**) &pAds);
  334. if ( SUCCEEDED(hr) )
  335. hr = pAds->Get(L"schemaNamingContext", &var);
  336. if ( SUCCEEDED(hr) )
  337. tgtSchema = var;
  338. else
  339. {
  340. if ( hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN) )
  341. {
  342. // for NT 4 domains, we always get this error
  343. _bstr_t strDc;
  344. DWORD rc = GetDcName4(tgtDomain, 0, strDc);
  345. if ( ! rc )
  346. {
  347. WKSTA_INFO_100 * pInfo = NULL;
  348. rc = NetWkstaGetInfo(strDc,100,(LPBYTE*)&pInfo);
  349. if ( ! rc )
  350. {
  351. if ( pInfo->wki100_ver_major < 5 )
  352. {
  353. (*pbIsSame) = FALSE;
  354. hr = 0;
  355. }
  356. NetApiBufferFree(pInfo);
  357. }
  358. else
  359. hr = HRESULT_FROM_WIN32(rc); // the return code from NetWkstaGetInfo may be more descriptive
  360. }
  361. else
  362. {
  363. hr = HRESULT_FROM_WIN32(rc);
  364. }
  365. }
  366. tgtSchema = L"";
  367. }
  368. if ( pAds )
  369. {
  370. pAds->Release();
  371. pAds = NULL;
  372. }
  373. *pbIsSame = (srcSchema == tgtSchema);
  374. }
  375. else
  376. {
  377. if ( hr == HRESULT_FROM_WIN32(ERROR_DS_SERVER_DOWN) )
  378. {
  379. // for NT 4 domains, we always get this error
  380. _bstr_t strDc;
  381. DWORD rc = GetDcName4(srcDomain, 0, strDc);
  382. if ( ! rc )
  383. {
  384. WKSTA_INFO_100 * pInfo = NULL;
  385. rc = NetWkstaGetInfo(strDc,100,(LPBYTE*)&pInfo);
  386. if ( ! rc )
  387. {
  388. if ( pInfo->wki100_ver_major < 5 )
  389. {
  390. (*pbIsSame) = FALSE;
  391. hr = 0;
  392. }
  393. NetApiBufferFree(pInfo);
  394. }
  395. else
  396. hr = HRESULT_FROM_WIN32(rc); // the return code from NetWkstaGetInfo may be more descriptive
  397. }
  398. }
  399. }
  400. return hr;
  401. }
  402. STDMETHODIMP
  403. CAccessChecker::GetPasswordPolicy(
  404. BSTR domain, /*[out]*/
  405. LONG * dwPasswordLength /*[out]*/
  406. )
  407. {
  408. HRESULT hr = S_OK;
  409. // initialize output parameter
  410. (*dwPasswordLength) = 0;
  411. ADSGETOBJECT ADsGetObject;
  412. HMODULE hMod = LoadLibrary(L"activeds.dll");
  413. if ( hMod == NULL )
  414. return HRESULT_FROM_WIN32(GetLastError());
  415. ADsGetObject = (ADSGETOBJECT)GetProcAddress(hMod, "ADsGetObject");
  416. if (!ADsGetObject)
  417. {
  418. if ( hMod )
  419. FreeLibrary(hMod);
  420. return HRESULT_FROM_WIN32(GetLastError());
  421. }
  422. IADsDomain * pDomain;
  423. _bstr_t sDom( L"WinNT://" );
  424. sDom += domain;
  425. hr = ADsGetObject(sDom, IID_IADsDomain, (void **) &pDomain);
  426. if (SUCCEEDED(hr))
  427. {
  428. //Get the ntMixedDomain attribute
  429. hr = pDomain->get_MinPasswordLength(dwPasswordLength);
  430. pDomain->Release();
  431. }
  432. if ( hMod )
  433. FreeLibrary(hMod);
  434. return hr;
  435. }
  436. STDMETHODIMP CAccessChecker::EnableAuditing(BSTR sDC)
  437. {
  438. LSA_OBJECT_ATTRIBUTES ObjectAttributes;
  439. DWORD wszStringLength;
  440. LSA_UNICODE_STRING lsaszServer;
  441. NTSTATUS ntsResult;
  442. LSA_HANDLE hPolicy;
  443. long rc = 0;
  444. // Object attributes are reserved, so initalize to zeroes.
  445. ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
  446. //Initialize an LSA_UNICODE_STRING structure to the server name.
  447. wszStringLength = wcslen((WCHAR*)sDC);
  448. lsaszServer.Buffer = (WCHAR*)sDC;
  449. lsaszServer.Length = (USHORT)wszStringLength * sizeof(WCHAR);
  450. lsaszServer.MaximumLength=(USHORT)wszStringLength * sizeof(WCHAR);
  451. // Attempt to open the policy.
  452. ntsResult = LsaOpenPolicy(
  453. &lsaszServer,
  454. &ObjectAttributes,
  455. POLICY_VIEW_AUDIT_INFORMATION | POLICY_VIEW_LOCAL_INFORMATION | POLICY_SET_AUDIT_REQUIREMENTS ,
  456. &hPolicy //recieves the policy handle
  457. );
  458. if ( !ntsResult )
  459. {
  460. // Ask for audit policy information
  461. PPOLICY_AUDIT_EVENTS_INFO info;
  462. ntsResult = LsaQueryInformationPolicy(hPolicy, PolicyAuditEventsInformation, (PVOID *)&info);
  463. if ( !ntsResult )
  464. {
  465. // turn on the audit mode.
  466. info->AuditingMode = TRUE;
  467. // turn on the success/failure for the Account management events
  468. info->EventAuditingOptions[AuditCategoryAccountManagement] = POLICY_AUDIT_EVENT_SUCCESS | POLICY_AUDIT_EVENT_FAILURE;
  469. ntsResult = LsaSetInformationPolicy(hPolicy, PolicyAuditEventsInformation, (PVOID) info);
  470. if ( ntsResult )
  471. rc = LsaNtStatusToWinError(ntsResult);
  472. // be a good boy and cleanup after yourself.
  473. LsaFreeMemory((PVOID) info);
  474. }
  475. else
  476. rc = LsaNtStatusToWinError(ntsResult);
  477. //Freeing the policy object handle
  478. ntsResult = LsaClose(hPolicy);
  479. }
  480. else
  481. rc = LsaNtStatusToWinError(ntsResult);
  482. // long rc = LsaNtStatusToWinError(ntsResult);
  483. return HRESULT_FROM_WIN32(rc);
  484. }
  485. long CAccessChecker::DetectAuditing(BSTR sDC)
  486. {
  487. LSA_OBJECT_ATTRIBUTES ObjectAttributes;
  488. DWORD wszStringLength;
  489. LSA_UNICODE_STRING lsaszServer;
  490. NTSTATUS ntsResult;
  491. LSA_HANDLE hPolicy;
  492. long rc = 0;
  493. // Object attributes are reserved, so initalize to zeroes.
  494. ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
  495. //Initialize an LSA_UNICODE_STRING structure to the server name.
  496. wszStringLength = wcslen((WCHAR*)sDC);
  497. lsaszServer.Buffer = (WCHAR*)sDC;
  498. lsaszServer.Length = (USHORT)wszStringLength * sizeof(WCHAR);
  499. lsaszServer.MaximumLength=(USHORT)wszStringLength * sizeof(WCHAR);
  500. // Attempt to open the policy.
  501. ntsResult = LsaOpenPolicy(
  502. &lsaszServer,
  503. &ObjectAttributes,
  504. POLICY_VIEW_AUDIT_INFORMATION | POLICY_VIEW_LOCAL_INFORMATION,
  505. &hPolicy //recieves the policy handle
  506. );
  507. if ( !ntsResult )
  508. {
  509. // Ask for audit policy information
  510. PPOLICY_AUDIT_EVENTS_INFO info;
  511. ntsResult = LsaQueryInformationPolicy(hPolicy, PolicyAuditEventsInformation, (PVOID *)&info);
  512. if ( !ntsResult )
  513. {
  514. // check if the over all auditing is turned on
  515. if (!info->AuditingMode)
  516. rc = -1;
  517. // Check if the account management event auditing is on
  518. if (info->EventAuditingOptions[AuditCategoryAccountManagement] != (POLICY_AUDIT_EVENT_SUCCESS | POLICY_AUDIT_EVENT_FAILURE))
  519. rc = -1;
  520. LsaFreeMemory((PVOID) info);
  521. }
  522. else
  523. rc = LsaNtStatusToWinError(ntsResult);
  524. //Freeing the policy object handle
  525. ntsResult = LsaClose(hPolicy);
  526. }
  527. else
  528. rc = LsaNtStatusToWinError(ntsResult);
  529. return rc;
  530. }
  531. STDMETHODIMP CAccessChecker::AddRegKey(BSTR srcDc,LONG bReboot)
  532. {
  533. // This function will add the necessary registry key and then reboot the
  534. // PDC for a given domain
  535. TRegKey sysKey, regComputer;
  536. DOMAIN_CONTROLLER_INFO * pSrcDomCtrlInfo = NULL;
  537. DWORD rc = 0; // OS return code
  538. // BSTR bstrSourceMachine = NULL;
  539. _bstr_t sDC;
  540. if (GetBkupRstrPriv(srcDc))
  541. {
  542. rc = regComputer.Connect( HKEY_LOCAL_MACHINE, (WCHAR*)srcDc );
  543. }
  544. else
  545. {
  546. rc = GetLastError();
  547. }
  548. // Add the TcpipClientSupport DWORD value
  549. if ( ! rc )
  550. {
  551. rc = sysKey.Open(L"System\\CurrentControlSet\\Control\\Lsa",&regComputer);
  552. }
  553. if ( ! rc )
  554. {
  555. rc = sysKey.ValueSetDWORD(L"TcpipClientSupport",1);
  556. }
  557. if ( !rc && bReboot)
  558. {
  559. // Computer will shutdown and restart in 10 seconds.
  560. rc = ComputerShutDown((WCHAR*) srcDc, GET_STRING(IDS_RegKeyRebootMessage), 10, TRUE, FALSE);
  561. }
  562. if ( pSrcDomCtrlInfo ) NetApiBufferFree(pSrcDomCtrlInfo);
  563. return HRESULT_FROM_WIN32(rc);
  564. }